forked from enlightenment/efl
ecore: Create Promises
Add a promise object that allows Eolian interface to include promises as a way to have asynchronous value return and composibility. The usage is like this in a .eo file: class Foo { methods { bar { params { promise: Promise<int>; } } } } Which will create the following API interface: void foo_bar(Ecore_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.
This commit is contained in:
parent
d723990955
commit
f9ba80ab33
|
@ -47,7 +47,8 @@ lib/ecore/Ecore.h \
|
|||
lib/ecore/Ecore_Common.h \
|
||||
lib/ecore/Ecore_Legacy.h \
|
||||
lib/ecore/Ecore_Eo.h \
|
||||
lib/ecore/Ecore_Getopt.h
|
||||
lib/ecore/Ecore_Getopt.h \
|
||||
lib/ecore/ecore_promise.h
|
||||
|
||||
nodist_installed_ecoremainheaders_DATA = \
|
||||
$(ecore_eolian_h)
|
||||
|
@ -72,6 +73,7 @@ lib/ecore/ecore_timer.c \
|
|||
lib/ecore/ecore_thread.c \
|
||||
lib/ecore/ecore_throttle.c \
|
||||
lib/ecore/ecore_exe.c \
|
||||
lib/ecore/ecore_promise.c \
|
||||
lib/ecore/ecore_exe_private.h \
|
||||
lib/ecore/ecore_private.h
|
||||
|
||||
|
@ -199,6 +201,7 @@ tests/ecore/ecore_test_animator.c \
|
|||
tests/ecore/ecore_test_ecore_thread_eina_thread_queue.c \
|
||||
tests/ecore/ecore_test_ecore_input.c \
|
||||
tests/ecore/ecore_test_ecore_file.c \
|
||||
tests/ecore/ecore_test_promise.c \
|
||||
tests/ecore/ecore_suite.h
|
||||
|
||||
tests_ecore_ecore_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
|
||||
|
|
|
@ -311,6 +311,9 @@ eo_bind_func_generate(const Eolian_Class *class, const Eolian_Function *funcid,
|
|||
if (ftype != EOLIAN_PROP_GET && ftype != EOLIAN_PROP_SET) ftype = eolian_function_type_get(funcid);
|
||||
Eina_Bool is_prop = (ftype == EOLIAN_PROP_GET || ftype == EOLIAN_PROP_SET);
|
||||
|
||||
Eina_Bool has_promise = EINA_FALSE;
|
||||
const char* promise_param_name = NULL;
|
||||
const char* promise_value_type = NULL;
|
||||
Eina_Bool need_implementation = EINA_TRUE;
|
||||
if (!impl_env && eolian_function_is_virtual_pure(funcid, ftype)) need_implementation = EINA_FALSE;
|
||||
|
||||
|
@ -337,9 +340,11 @@ eo_bind_func_generate(const Eolian_Class *class, const Eolian_Function *funcid,
|
|||
if (eina_iterator_next(itr, &data) && !eina_iterator_next(itr, &data2))
|
||||
{
|
||||
Eolian_Function_Parameter *param = data;
|
||||
const char* rettype_str = NULL;
|
||||
rettypet = eolian_parameter_type_get(param);
|
||||
var_as_ret = EINA_TRUE;
|
||||
default_ret_val = eolian_parameter_default_value_get(param);
|
||||
eina_stringshare_del(rettype_str);
|
||||
}
|
||||
eina_iterator_free(itr);
|
||||
}
|
||||
|
@ -375,9 +380,24 @@ eo_bind_func_generate(const Eolian_Class *class, const Eolian_Function *funcid,
|
|||
const char *ptype = eolian_type_c_type_get(ptypet);
|
||||
Eolian_Parameter_Dir pdir = eolian_parameter_direction_get(param);
|
||||
Eina_Bool had_star = !!strchr(ptype, '*');
|
||||
|
||||
if(!has_promise && !strcmp(ptype, "Ecore_Promise *"))
|
||||
{
|
||||
Eina_Iterator* promise_values;
|
||||
has_promise = EINA_TRUE;
|
||||
promise_param_name = eina_stringshare_add(pname);
|
||||
promise_values = eolian_type_subtypes_get(eolian_type_base_type_get(ptypet));
|
||||
Eolian_Type* subtype;
|
||||
if(eina_iterator_next(promise_values, (void**)&subtype))
|
||||
promise_value_type = eolian_type_c_type_get(subtype);
|
||||
}
|
||||
|
||||
if (ftype == EOLIAN_UNRESOLVED || ftype == EOLIAN_METHOD) add_star = (pdir == EOLIAN_OUT_PARAM || pdir == EOLIAN_INOUT_PARAM);
|
||||
if (eina_strbuf_length_get(params)) eina_strbuf_append(params, ", ");
|
||||
eina_strbuf_append_printf(params, "%s", pname);
|
||||
if(has_promise)
|
||||
eina_strbuf_append_printf(params, "%s", "&__eo_promise");
|
||||
else
|
||||
eina_strbuf_append_printf(params, "%s", pname);
|
||||
eina_strbuf_append_printf(full_params, ", %s%s%s%s%s",
|
||||
ptype, had_star?"":" ", add_star?"*":"", pname, is_empty && !dflt_value ?" EINA_UNUSED":"");
|
||||
if (is_auto)
|
||||
|
@ -511,8 +531,8 @@ eo_bind_func_generate(const Eolian_Class *class, const Eolian_Function *funcid,
|
|||
Eina_Bool ret_is_void = (!rettype || !strcmp(rettype, "void"));
|
||||
_class_func_env_create(class, eolian_function_name_get(funcid), ftype, &func_env);
|
||||
eina_strbuf_append_printf(eo_func_decl,
|
||||
"EOAPI EO_%sFUNC_BODY%s%s(%s",
|
||||
ret_is_void?"VOID_":"", has_params?"V":"",
|
||||
"EOAPI EO_%sFUNC_%sBODY%s%s(%s",
|
||||
ret_is_void?"VOID_":"", has_promise?"PROMISE_":"", has_params?"V":"",
|
||||
(ftype == EOLIAN_PROP_GET ||
|
||||
eolian_function_object_is_const(funcid) ||
|
||||
eolian_function_is_class(funcid))?"_CONST":"", func_env.lower_eo_func);
|
||||
|
@ -535,6 +555,10 @@ eo_bind_func_generate(const Eolian_Class *class, const Eolian_Function *funcid,
|
|||
eina_stringshare_del(string);
|
||||
}
|
||||
}
|
||||
if (has_promise)
|
||||
{
|
||||
eina_strbuf_append_printf(eo_func_decl, ", %s, %s", promise_param_name, promise_value_type);
|
||||
}
|
||||
if (has_params)
|
||||
{
|
||||
eina_strbuf_replace_all(full_params, " EINA_UNUSED", "");
|
||||
|
|
|
@ -356,6 +356,7 @@ extern "C" {
|
|||
#endif
|
||||
#ifdef EFL_EO_API_SUPPORT
|
||||
#include "Ecore_Eo.h"
|
||||
#include "ecore_promise.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -0,0 +1,452 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <Eina.h>
|
||||
#include <Ecore.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
typedef void(*Ecore_Promise_Free_Cb)(void*);
|
||||
|
||||
struct _Ecore_Promise_Then_Cb
|
||||
{
|
||||
EINA_INLIST;
|
||||
|
||||
Ecore_Promise_Cb callback;
|
||||
void* data;
|
||||
};
|
||||
|
||||
struct _Ecore_Promise
|
||||
{
|
||||
Eina_Lock lock;
|
||||
Eina_Condition condition;
|
||||
Eina_Bool has_finished : 1;
|
||||
Eina_Bool has_errored : 1;
|
||||
Eina_Bool has_pending_call : 1;
|
||||
Eina_Bool is_then_calls_manual : 1;
|
||||
Eina_Error error;
|
||||
size_t value_size;
|
||||
int ref;
|
||||
|
||||
struct _Ecore_Promise_Then_Cb then_callbacks;
|
||||
Ecore_Promise_Free_Cb free_cb;
|
||||
|
||||
char value[];
|
||||
};
|
||||
|
||||
struct _Ecore_Promise_Thread_Data
|
||||
{
|
||||
const void* data;
|
||||
Ecore_Promise_Thread_Cb func_blocking;
|
||||
Ecore_Promise* promise;
|
||||
};
|
||||
|
||||
typedef struct _Ecore_Promise_Iterator _Ecore_Promise_Iterator;
|
||||
typedef struct _Ecore_Promise_Success_Iterator _Ecore_Promise_Success_Iterator;
|
||||
typedef struct _Ecore_Promise_Failure_Iterator _Ecore_Promise_Failure_Iterator;
|
||||
struct _Ecore_Promise_Iterator
|
||||
{
|
||||
Eina_Iterator* success_iterator;
|
||||
Eina_Iterator* failure_iterator;
|
||||
struct _Ecore_Promise_Success_Iterator
|
||||
{
|
||||
Eina_Iterator success_iterator_impl;
|
||||
struct _Ecore_Promise_Failure_Iterator
|
||||
{
|
||||
Eina_Iterator failure_iterator_impl;
|
||||
unsigned int promise_index;
|
||||
unsigned int num_promises;
|
||||
unsigned int promises_finished;
|
||||
Ecore_Promise* promises[];
|
||||
} data;
|
||||
} data;
|
||||
};
|
||||
|
||||
static void _ecore_promise_lock_take(Ecore_Promise* promise);
|
||||
static void _ecore_promise_lock_release(Ecore_Promise* promise);
|
||||
static void _ecore_promise_finish(Ecore_Promise* promise);
|
||||
static void _ecore_promise_then_calls(Ecore_Promise* promise);
|
||||
static void _ecore_promise_unsafe_ref(Ecore_Promise const* promise);
|
||||
static void _ecore_promise_unsafe_unref(Ecore_Promise const* promise);
|
||||
static void _ecore_promise_unsafe_free_unref(Ecore_Promise const* promise);
|
||||
static Eina_Bool _ecore_promise_unlock_unsafe_free_unref(Ecore_Promise const* promise);
|
||||
|
||||
static void _ecore_promise_iterator_setup(_Ecore_Promise_Iterator* iterator, Eina_Array* promises);
|
||||
|
||||
static void _ecore_promise_thread_end(void* data, Ecore_Thread* thread EINA_UNUSED)
|
||||
{
|
||||
struct _Ecore_Promise_Thread_Data* p = data;
|
||||
_ecore_promise_lock_take(p->promise);
|
||||
if(p->promise->has_finished)
|
||||
{
|
||||
p->promise->has_pending_call = EINA_FALSE;
|
||||
if(!_ecore_promise_unlock_unsafe_free_unref(p->promise))
|
||||
_ecore_promise_then_calls(p->promise);
|
||||
}
|
||||
else
|
||||
{
|
||||
p->promise->is_then_calls_manual = EINA_FALSE;
|
||||
p->promise->has_pending_call = EINA_FALSE;
|
||||
_ecore_promise_unlock_unsafe_free_unref(p->promise);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void
|
||||
_ecore_promise_thread_blocking(void* data, Ecore_Thread* thread EINA_UNUSED)
|
||||
{
|
||||
struct _Ecore_Promise_Thread_Data* p = data;
|
||||
(p->func_blocking)(p->data, p->promise);
|
||||
}
|
||||
|
||||
static void
|
||||
_ecore_promise_then_calls(Ecore_Promise* promise)
|
||||
{
|
||||
_ecore_promise_lock_take(promise);
|
||||
struct _Ecore_Promise_Then_Cb then_callbacks = promise->then_callbacks;
|
||||
memset(&promise->then_callbacks, 0, sizeof(promise->then_callbacks));
|
||||
promise->has_pending_call = EINA_FALSE;
|
||||
_ecore_promise_lock_release(promise);
|
||||
|
||||
struct _Ecore_Promise_Then_Cb* callback;
|
||||
|
||||
if(then_callbacks.callback)
|
||||
{
|
||||
(*then_callbacks.callback)(then_callbacks.data, &promise->value[0]);
|
||||
_ecore_promise_unsafe_free_unref(promise);
|
||||
}
|
||||
|
||||
if(EINA_INLIST_GET(&then_callbacks)->next)
|
||||
{
|
||||
Eina_Inlist* list2;
|
||||
EINA_INLIST_FOREACH_SAFE(EINA_INLIST_GET(&then_callbacks)->next, list2, callback)
|
||||
{
|
||||
if(callback->callback)
|
||||
{
|
||||
(*callback->callback)(callback->data, promise);
|
||||
}
|
||||
_ecore_promise_unsafe_free_unref(promise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ecore_Promise* ecore_promise_thread_run(Ecore_Promise_Thread_Cb func_blocking, const void* data, size_t value_size)
|
||||
{
|
||||
struct _Ecore_Promise_Thread_Data *new_data = malloc(sizeof(struct _Ecore_Promise_Thread_Data));
|
||||
new_data->data = data;
|
||||
new_data->func_blocking = func_blocking;
|
||||
new_data->promise = ecore_promise_add(value_size);
|
||||
new_data->promise->is_then_calls_manual = EINA_TRUE;
|
||||
new_data->promise->has_pending_call = EINA_TRUE;
|
||||
ecore_promise_ref(new_data->promise);
|
||||
ecore_thread_run(&_ecore_promise_thread_blocking, &_ecore_promise_thread_end, NULL, new_data);
|
||||
return new_data->promise;
|
||||
}
|
||||
|
||||
Ecore_Promise* ecore_promise_add(int value_size)
|
||||
{
|
||||
Ecore_Promise* p = malloc(sizeof(Ecore_Promise) + value_size);
|
||||
eina_lock_new(&p->lock);
|
||||
eina_condition_new(&p->condition, &p->lock);
|
||||
p->has_finished = p->has_errored = p->has_pending_call = p->is_then_calls_manual = EINA_FALSE;
|
||||
p->ref = 1;
|
||||
memset(&p->then_callbacks, 0, sizeof(p->then_callbacks));
|
||||
p->value_size = value_size;
|
||||
p->free_cb = NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void _ecore_promise_del(Ecore_Promise* promise)
|
||||
{
|
||||
if(promise->free_cb)
|
||||
promise->free_cb((void*)&promise->value[0]);
|
||||
|
||||
eina_lock_free(&promise->lock);
|
||||
eina_condition_free(&promise->condition);
|
||||
}
|
||||
|
||||
void* ecore_promise_buffer_get(Ecore_Promise* promise)
|
||||
{
|
||||
return &promise->value[0];
|
||||
}
|
||||
|
||||
void* ecore_promise_value_get(Ecore_Promise const* promise)
|
||||
{
|
||||
_ecore_promise_lock_take((Ecore_Promise*)promise);
|
||||
_ecore_promise_unsafe_ref(promise);
|
||||
while(!promise->has_finished)
|
||||
{
|
||||
eina_condition_wait((Eina_Condition*)&promise->condition);
|
||||
}
|
||||
|
||||
void* v = (void*)(promise->value_size && !promise->has_errored ? &promise->value[0] : NULL);
|
||||
_ecore_promise_unsafe_unref(promise);
|
||||
_ecore_promise_lock_release((Ecore_Promise*)promise);
|
||||
return v;
|
||||
}
|
||||
|
||||
void ecore_promise_value_set(Ecore_Promise* promise, void* data)
|
||||
{
|
||||
_ecore_promise_lock_take(promise);
|
||||
if(data && promise->value_size)
|
||||
{
|
||||
memcpy(&promise->value[0], data, promise->value_size);
|
||||
}
|
||||
|
||||
_ecore_promise_finish(promise);
|
||||
}
|
||||
|
||||
static void _ecore_promise_all_compose_then_cb(Ecore_Promise* promise, void* value EINA_UNUSED)
|
||||
{
|
||||
_ecore_promise_lock_take(promise);
|
||||
_Ecore_Promise_Iterator* iterator = (_Ecore_Promise_Iterator*)promise->value;
|
||||
|
||||
if(++iterator->data.data.promises_finished == iterator->data.data.num_promises)
|
||||
{
|
||||
_ecore_promise_finish(promise);
|
||||
}
|
||||
else
|
||||
_ecore_promise_lock_release(promise);
|
||||
}
|
||||
|
||||
static void _ecore_promise_all_free(_Ecore_Promise_Iterator* value)
|
||||
{
|
||||
unsigned i = 0;
|
||||
eina_iterator_free(value->success_iterator);
|
||||
/* eina_iterator_free(value->failure_iterator); */
|
||||
|
||||
for(;i != value->data.data.num_promises; ++i)
|
||||
{
|
||||
ecore_promise_unref(value->data.data.promises[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Ecore_Promise* ecore_promise_all(Eina_Iterator* it)
|
||||
{
|
||||
Ecore_Promise* current, *promise;
|
||||
Eina_Array* promises;
|
||||
|
||||
promises = eina_array_new(20);
|
||||
|
||||
EINA_ITERATOR_FOREACH(it, current)
|
||||
{
|
||||
eina_array_push(promises, current);
|
||||
}
|
||||
|
||||
promise = ecore_promise_add(sizeof(_Ecore_Promise_Iterator) + sizeof(Ecore_Promise*)*eina_array_count_get(promises));
|
||||
//promise->is_then_calls_manual = EINA_TRUE;
|
||||
promise->free_cb = (Ecore_Promise_Free_Cb)_ecore_promise_all_free;
|
||||
_Ecore_Promise_Iterator* internal_it = ecore_promise_buffer_get(promise);
|
||||
_ecore_promise_iterator_setup(internal_it, promises);
|
||||
eina_array_free(promises);
|
||||
|
||||
{
|
||||
Ecore_Promise** cur_promise = internal_it->data.data.promises, ** last =
|
||||
internal_it->data.data.promises + internal_it->data.data.num_promises;
|
||||
for(;cur_promise != last; ++cur_promise)
|
||||
{
|
||||
ecore_promise_ref(*cur_promise);
|
||||
ecore_promise_then(*cur_promise, (Ecore_Promise_Cb)&_ecore_promise_all_compose_then_cb, promise);
|
||||
}
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
void ecore_promise_then(Ecore_Promise* promise, Ecore_Promise_Cb callback, void* data)
|
||||
{
|
||||
_ecore_promise_lock_take(promise);
|
||||
_ecore_promise_unsafe_ref(promise);
|
||||
if(!promise->then_callbacks.callback && !EINA_INLIST_GET(&promise->then_callbacks)->next)
|
||||
{
|
||||
promise->then_callbacks.callback = callback;
|
||||
promise->then_callbacks.data = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct _Ecore_Promise_Then_Cb* p = malloc(sizeof(struct _Ecore_Promise_Then_Cb));
|
||||
p->callback = callback;
|
||||
p->data = data;
|
||||
Eina_Inlist* l = eina_inlist_append(EINA_INLIST_GET(&promise->then_callbacks), EINA_INLIST_GET(p));
|
||||
(void)l;
|
||||
}
|
||||
if(promise->has_finished && !promise->has_pending_call)
|
||||
{
|
||||
promise->has_pending_call = EINA_TRUE;
|
||||
_ecore_promise_lock_release(promise);
|
||||
ecore_job_add((Ecore_Cb)&_ecore_promise_then_calls, promise);
|
||||
}
|
||||
else
|
||||
_ecore_promise_lock_release(promise);
|
||||
}
|
||||
|
||||
EAPI Eina_Error ecore_promise_error_get(Ecore_Promise const* promise)
|
||||
{
|
||||
_ecore_promise_lock_take((Ecore_Promise*)promise);
|
||||
if(promise->has_errored)
|
||||
{
|
||||
Eina_Error error = promise->error;
|
||||
_ecore_promise_lock_release((Ecore_Promise*)promise);
|
||||
return error;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ecore_promise_lock_release((Ecore_Promise*)promise);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
EAPI void ecore_promise_error_set(Ecore_Promise* promise, Eina_Error error)
|
||||
{
|
||||
_ecore_promise_lock_take(promise);
|
||||
promise->error = error;
|
||||
promise->has_errored = EINA_TRUE;
|
||||
|
||||
_ecore_promise_finish(promise);
|
||||
}
|
||||
|
||||
static void
|
||||
_ecore_promise_finish(Ecore_Promise* promise)
|
||||
{
|
||||
promise->has_finished = EINA_TRUE;
|
||||
eina_condition_broadcast(&promise->condition);
|
||||
_ecore_promise_unsafe_unref(promise);
|
||||
if(!promise->is_then_calls_manual && !promise->has_pending_call)
|
||||
{
|
||||
promise->has_pending_call = EINA_TRUE;
|
||||
_ecore_promise_lock_release(promise);
|
||||
ecore_job_add((Ecore_Cb)&_ecore_promise_then_calls, promise);
|
||||
}
|
||||
else
|
||||
_ecore_promise_lock_release(promise);
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_ecore_promise_iterator_next(_Ecore_Promise_Success_Iterator *it, void **data)
|
||||
{
|
||||
if(it->data.promise_index == it->data.num_promises)
|
||||
return EINA_FALSE;
|
||||
|
||||
if(ecore_promise_error_get(it->data.promises[it->data.promise_index]))
|
||||
{
|
||||
return EINA_FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
*data = ecore_promise_value_get(it->data.promises[it->data.promise_index++]);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void**
|
||||
_ecore_promise_iterator_get_container(_Ecore_Promise_Success_Iterator *it)
|
||||
{
|
||||
return (void**)it->data.promises;
|
||||
}
|
||||
|
||||
static void
|
||||
_ecore_promise_iterator_free(_Ecore_Promise_Success_Iterator *it EINA_UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
static void _ecore_promise_iterator_setup(_Ecore_Promise_Iterator* it, Eina_Array* promises_array)
|
||||
{
|
||||
Ecore_Promise** promises;
|
||||
|
||||
it->success_iterator = &it->data.success_iterator_impl;
|
||||
it->failure_iterator = &it->data.data.failure_iterator_impl;
|
||||
it->data.data.num_promises = eina_array_count_get(promises_array);
|
||||
it->data.data.promise_index = 0;
|
||||
promises = (Ecore_Promise**)promises_array->data;
|
||||
|
||||
memcpy(&it->data.data.promises[0], promises, it->data.data.num_promises*sizeof(Ecore_Promise*));
|
||||
|
||||
EINA_MAGIC_SET(&it->data.success_iterator_impl, EINA_MAGIC_ITERATOR);
|
||||
EINA_MAGIC_SET(&it->data.data.failure_iterator_impl, EINA_MAGIC_ITERATOR);
|
||||
|
||||
it->data.success_iterator_impl.version = EINA_ITERATOR_VERSION;
|
||||
it->data.success_iterator_impl.next = FUNC_ITERATOR_NEXT(_ecore_promise_iterator_next);
|
||||
it->data.success_iterator_impl.get_container = FUNC_ITERATOR_GET_CONTAINER(
|
||||
_ecore_promise_iterator_get_container);
|
||||
it->data.success_iterator_impl.free = FUNC_ITERATOR_FREE(_ecore_promise_iterator_free);
|
||||
}
|
||||
|
||||
EAPI int ecore_promise_value_size_get(Ecore_Promise const* promise)
|
||||
{
|
||||
return promise->value_size;
|
||||
}
|
||||
|
||||
static void _ecore_promise_lock_take(Ecore_Promise* promise)
|
||||
{
|
||||
eina_lock_take(&promise->lock);
|
||||
}
|
||||
|
||||
static void _ecore_promise_lock_release(Ecore_Promise* promise)
|
||||
{
|
||||
eina_lock_release(&promise->lock);
|
||||
}
|
||||
|
||||
static void _ecore_promise_unsafe_ref(Ecore_Promise const* promise)
|
||||
{
|
||||
Ecore_Promise* p = (Ecore_Promise*)promise;
|
||||
++p->ref;
|
||||
}
|
||||
|
||||
static void _ecore_promise_free_cb(Ecore_Promise* promise)
|
||||
{
|
||||
_ecore_promise_lock_take(promise);
|
||||
_ecore_promise_unlock_unsafe_free_unref(promise);
|
||||
}
|
||||
|
||||
static void _ecore_promise_unsafe_unref(Ecore_Promise const* promise)
|
||||
{
|
||||
Ecore_Promise* p = (Ecore_Promise*)promise;
|
||||
if(p->ref == 1 && !p->has_pending_call)
|
||||
{
|
||||
ecore_job_add((Ecore_Cb)_ecore_promise_free_cb, p);
|
||||
}
|
||||
else
|
||||
--p->ref;
|
||||
}
|
||||
|
||||
static void _ecore_promise_unsafe_free_unref(Ecore_Promise const* promise)
|
||||
{
|
||||
Ecore_Promise* p = (Ecore_Promise*)promise;
|
||||
if(--p->ref == 0)
|
||||
{
|
||||
assert(!p->has_pending_call);
|
||||
_ecore_promise_del(p);
|
||||
}
|
||||
}
|
||||
|
||||
static Eina_Bool _ecore_promise_unlock_unsafe_free_unref(Ecore_Promise const* promise)
|
||||
{
|
||||
Ecore_Promise* p = (Ecore_Promise*)promise;
|
||||
if(--p->ref == 0)
|
||||
{
|
||||
assert(!p->has_pending_call);
|
||||
_ecore_promise_lock_release((Ecore_Promise*)promise);
|
||||
_ecore_promise_del(p);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
_ecore_promise_lock_release((Ecore_Promise*)promise);
|
||||
return EINA_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
EAPI void ecore_promise_ref(Ecore_Promise* promise)
|
||||
{
|
||||
_ecore_promise_lock_take(promise);
|
||||
_ecore_promise_unsafe_ref(promise);
|
||||
_ecore_promise_lock_release(promise);
|
||||
}
|
||||
|
||||
EAPI void ecore_promise_unref(Ecore_Promise* promise)
|
||||
{
|
||||
_ecore_promise_lock_take(promise);
|
||||
_ecore_promise_unsafe_unref(promise);
|
||||
_ecore_promise_lock_release(promise);
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
|
||||
#ifdef EFL_BETA_API_SUPPORT
|
||||
|
||||
struct _Ecore_Promise;
|
||||
|
||||
/*
|
||||
* @def _Ecore_Promise
|
||||
*/
|
||||
typedef struct _Ecore_Promise Ecore_Promise;
|
||||
|
||||
/*
|
||||
* @brief Function callback type for when using ecore_promise_then
|
||||
*/
|
||||
typedef void(*Ecore_Promise_Cb)(void* data, void* value);
|
||||
|
||||
/*
|
||||
* @brief Function callback type for when creating Ecore_Thread that
|
||||
* uses Ecore_Promise for communication
|
||||
*/
|
||||
typedef void(*Ecore_Promise_Thread_Cb)(const void* data, Ecore_Promise* promise);
|
||||
|
||||
/*
|
||||
* @brief Function that instantiates a Ecore_Promise and automatically
|
||||
* executes func_blocking callback function in another thread
|
||||
*/
|
||||
EAPI Ecore_Promise* ecore_promise_thread_run(Ecore_Promise_Thread_Cb func_blocking, const void* data, size_t value_size);
|
||||
|
||||
/*
|
||||
* @brief Creates a Ecore_Promise with a value of size value_size.
|
||||
*
|
||||
* @param value_size Size of value-type that Ecore_Promise will hold
|
||||
*/
|
||||
EAPI Ecore_Promise* ecore_promise_add(int value_size);
|
||||
|
||||
/*
|
||||
* @brief Appends a callback to be called when the Ecore_Promise is
|
||||
* finished.
|
||||
*
|
||||
* @param promise The Ecore_Promise to wait for
|
||||
* @param callback Callback to be called when Ecore_Promise is finished
|
||||
* @param data Private data passed to the callback
|
||||
*/
|
||||
EAPI void ecore_promise_then(Ecore_Promise* promise, Ecore_Promise_Cb callback, void* data);
|
||||
|
||||
/*
|
||||
* @brief Creates a new Ecore_Promise from other Ecore_Promises
|
||||
*
|
||||
* @param promises An Eina_Iterator for all Ecore_Promises
|
||||
*/
|
||||
EAPI Ecore_Promise* ecore_promise_all(Eina_Iterator* promises);
|
||||
|
||||
/*
|
||||
* @brief Sets value for Ecore_Promise. This finishes the callback and
|
||||
* calls all ecore_promise_then callbacks that have been registered on
|
||||
* this Ecore_Promise. This function must be called only once per
|
||||
* Ecore_Promise
|
||||
*
|
||||
* @param promise The promise for which to set the value
|
||||
* @param value The pointer to the value that is going to be copied, or NULL.
|
||||
*/
|
||||
EAPI void ecore_promise_value_set(Ecore_Promise* promise, void* value);
|
||||
|
||||
/*
|
||||
* @brief Returns the pointer to the value if the Ecore_Promise is
|
||||
* finished. Waits for it to be finished, otherwise.
|
||||
*
|
||||
* @param promise The promise for which to get the value
|
||||
*/
|
||||
EAPI void* ecore_promise_value_get(Ecore_Promise const* promise);
|
||||
|
||||
/*
|
||||
* @brief Returns the pointer to the buffer that holds the value. This
|
||||
* function is useful to instantiate the value directly in the correct
|
||||
* buffer, without needing to copy. The ecore_promise_value_set must
|
||||
* still be called, possibly with NULL, to finish the Ecore_Promise
|
||||
* and call the callbacks registered in it.
|
||||
*
|
||||
* @param promise The promise for which to get the buffer pointer
|
||||
*/
|
||||
EAPI void* ecore_promise_buffer_get(Ecore_Promise* promise);
|
||||
|
||||
/*
|
||||
* @brief Sets an error to the Ecore_Promise, thus finishing the
|
||||
* promise and calling all ecore_promise_then callbacks registered.
|
||||
*
|
||||
* @param promise The promise for which to set the error
|
||||
* @param error Eina_Error to be set
|
||||
*/
|
||||
EAPI void ecore_promise_error_set(Ecore_Promise* promise, Eina_Error error);
|
||||
|
||||
/*
|
||||
* @brief Gets an error to the Ecore_Promise if the promise is
|
||||
* finished and has error'ed out. If it hasn't finished, it will wait,
|
||||
* and if it has finished but otherwise not error'ed, returns 0.
|
||||
*
|
||||
* @param promise The promise for which to get the error
|
||||
*/
|
||||
EAPI Eina_Error ecore_promise_error_get(Ecore_Promise const* promise);
|
||||
|
||||
/*
|
||||
* @brief Gets the size of the value in ecore_promise_value_get.
|
||||
*
|
||||
* @param promise The promise for which to get the value size
|
||||
*/
|
||||
EAPI int ecore_promise_value_size_get(Ecore_Promise const* promise);
|
||||
|
||||
/*
|
||||
* @brief Returns @EINA_TRUE if the promise is ready and won't block
|
||||
* on ecore_promise_value_get and @EINA_FALSE otherwise.
|
||||
*
|
||||
* @param promise The promise for which to get the ready status
|
||||
*/
|
||||
EAPI Eina_Bool ecore_promise_ready_is(Ecore_Promise const* promise);
|
||||
|
||||
/*
|
||||
* @brief Increments the reference count for the Ecore_Promise
|
||||
*
|
||||
* @param promise The promise for which to increment its reference
|
||||
*/
|
||||
EAPI void ecore_promise_ref(Ecore_Promise* promise);
|
||||
|
||||
/*
|
||||
* @brief Decrement the reference count for the Ecore_Promise and
|
||||
* possibly schedule its destruction. The Ecore_Promise, if its
|
||||
* reference count drops to zero, will only be free'd when all the
|
||||
* current mainloop events have been processed. This allows the user
|
||||
* to call ecore_promise_then before that happens so it can increment
|
||||
* the reference back to 1 and wait for a value set or error set on
|
||||
* the Ecore_Promise.
|
||||
*
|
||||
* @param promise The promise for which to decrement its reference
|
||||
*/
|
||||
EAPI void ecore_promise_unref(Ecore_Promise* promise);
|
||||
|
||||
#endif
|
||||
|
|
@ -511,61 +511,96 @@ typedef struct _Eo_Call_Cache
|
|||
__FILE__, __LINE__)) return DefRet; \
|
||||
_Eo_##Name##_func _func_ = (_Eo_##Name##_func) ___call.func; \
|
||||
|
||||
#define _EO_FUNC_PROMISE_CREATE0
|
||||
#define _EO_FUNC_PROMISE_FREE0
|
||||
#define _EO_FUNC_PROMISE_CREATE1 \
|
||||
Ecore_Promise*(*ecore_promise_add)(int size) = dlsym(dlopen(NULL, RTLD_NOW), "ecore_promise_add"); \
|
||||
Ecore_Promise* __eo_promise = ecore_promise_add(sizeof(PromiseValue));
|
||||
#define _EO_FUNC_PROMISE_FREE1 \
|
||||
if(Promise) \
|
||||
*Promise = __eo_promise; \
|
||||
else \
|
||||
{ \
|
||||
void(*ecore_promise_unref)(Ecore_Promise* p) = dlsym(dlopen(NULL, RTLD_NOW), "ecore_promise_unref"); \
|
||||
ecore_promise_unref(__eo_promise); \
|
||||
}
|
||||
#define _EO_EXPANSION_AUX(X) X
|
||||
#define _EO_FUNC_PROMISE_CREATE(p) _EO_EXPANSION_AUX(_EO_FUNC_PROMISE_CREATE ## p)
|
||||
#define _EO_FUNC_PROMISE_FREE(p) _EO_EXPANSION_AUX(_EO_FUNC_PROMISE_FREE ## p)
|
||||
|
||||
// to define an EAPI function
|
||||
#define _EO_FUNC_BODY(Name, ObjType, Ret, DefRet) \
|
||||
#define _EO_FUNC_BODY(Name, ObjType, Promise, Ret, DefRet) \
|
||||
Ret \
|
||||
Name(ObjType obj) \
|
||||
{ \
|
||||
typedef Ret (*_Eo_##Name##_func)(Eo *, void *obj_data); \
|
||||
Ret _r; \
|
||||
EO_FUNC_COMMON_OP(obj, Name, DefRet); \
|
||||
_EO_FUNC_PROMISE_CREATE(Promise) \
|
||||
_r = _func_(___call.eo_id, ___call.data); \
|
||||
_eo_call_end(&___call); \
|
||||
_EO_FUNC_PROMISE_FREE(Promise) \
|
||||
return _r; \
|
||||
}
|
||||
|
||||
#define _EO_VOID_FUNC_BODY(Name, ObjType) \
|
||||
#define _EO_VOID_FUNC_BODY(Name, ObjType, Promise) \
|
||||
void \
|
||||
Name(ObjType obj) \
|
||||
{ \
|
||||
typedef void (*_Eo_##Name##_func)(Eo *, void *obj_data); \
|
||||
EO_FUNC_COMMON_OP(obj, Name, ); \
|
||||
_EO_FUNC_PROMISE_CREATE(Promise) \
|
||||
_func_(___call.eo_id, ___call.data); \
|
||||
_eo_call_end(&___call); \
|
||||
_EO_FUNC_PROMISE_FREE(Promise) \
|
||||
}
|
||||
|
||||
#define _EO_FUNC_BODYV(Name, ObjType, Ret, DefRet, Arguments, ...) \
|
||||
#define _EO_FUNC_BODYV(Name, ObjType, Promise, Ret, DefRet, Arguments, ...) \
|
||||
Ret \
|
||||
Name(ObjType obj, __VA_ARGS__) \
|
||||
{ \
|
||||
typedef Ret (*_Eo_##Name##_func)(Eo *, void *obj_data, __VA_ARGS__); \
|
||||
Ret _r; \
|
||||
EO_FUNC_COMMON_OP(obj, Name, DefRet); \
|
||||
_EO_FUNC_PROMISE_CREATE(Promise) \
|
||||
_r = _func_(___call.eo_id, ___call.data, Arguments); \
|
||||
_eo_call_end(&___call); \
|
||||
_EO_FUNC_PROMISE_FREE(Promise) \
|
||||
return _r; \
|
||||
}
|
||||
|
||||
#define _EO_VOID_FUNC_BODYV(Name, ObjType, Arguments, ...) \
|
||||
#define _EO_VOID_FUNC_BODYV(Name, ObjType, Promise, Arguments, ...) \
|
||||
void \
|
||||
Name(ObjType obj, __VA_ARGS__) \
|
||||
{ \
|
||||
typedef void (*_Eo_##Name##_func)(Eo *, void *obj_data, __VA_ARGS__); \
|
||||
EO_FUNC_COMMON_OP(obj, Name, ); \
|
||||
_EO_FUNC_PROMISE_CREATE(Promise) \
|
||||
_func_(___call.eo_id, ___call.data, Arguments); \
|
||||
_eo_call_end(&___call); \
|
||||
_EO_FUNC_PROMISE_FREE(Promise) \
|
||||
}
|
||||
|
||||
#define EO_FUNC_BODY(Name, Ret, DefRet) _EO_FUNC_BODY(Name, Eo *, Ret, DefRet)
|
||||
#define EO_VOID_FUNC_BODY(Name) _EO_VOID_FUNC_BODY(Name, Eo *)
|
||||
#define EO_FUNC_BODYV(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, Eo *, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
#define EO_VOID_FUNC_BODYV(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, Eo *, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
#define EO_FUNC_BODY(Name, Ret, DefRet) _EO_FUNC_BODY(Name, Eo *, 0, Ret, DefRet)
|
||||
#define EO_VOID_FUNC_BODY(Name) _EO_VOID_FUNC_BODY(Name, Eo *, 0)
|
||||
#define EO_FUNC_BODYV(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, Eo *, 0, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
#define EO_VOID_FUNC_BODYV(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, Eo *, 0, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
|
||||
#define EO_FUNC_BODY_CONST(Name, Ret, DefRet) _EO_FUNC_BODY(Name, const Eo *, Ret, DefRet)
|
||||
#define EO_VOID_FUNC_BODY_CONST(Name) _EO_VOID_FUNC_BODY(Name, const Eo *)
|
||||
#define EO_FUNC_BODYV_CONST(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, const Eo *, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
#define EO_VOID_FUNC_BODYV_CONST(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, const Eo *, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
#define EO_FUNC_BODY_CONST(Name, Ret, DefRet) _EO_FUNC_BODY(Name, const Eo *, 0, Ret, DefRet)
|
||||
#define EO_VOID_FUNC_BODY_CONST(Name) _EO_VOID_FUNC_BODY(Name, const Eo *, 0)
|
||||
#define EO_FUNC_BODYV_CONST(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, const Eo *, 0, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
#define EO_VOID_FUNC_BODYV_CONST(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, const Eo *, 0, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
|
||||
#define EO_FUNC_PROMISE_BODY(Name, Ret, DefRet) _EO_FUNC_BODY(Name, Eo *, 1, Ret, DefRet)
|
||||
#define EO_VOID_FUNC_PROMISE_BODY(Name) _EO_VOID_FUNC_BODY(Name, Eo *, 1)
|
||||
#define EO_FUNC_PROMISE_BODYV(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, Eo *, 1, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
#define EO_VOID_FUNC_PROMISE_BODYV(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, Eo *, 1, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
|
||||
#define EO_FUNC_PROMISE_BODY_CONST(Name, Ret, DefRet) _EO_FUNC_BODY(Name, const Eo *, 1, Ret, DefRet)
|
||||
#define EO_VOID_FUNC_PROMISE_BODY_CONST(Name) _EO_VOID_FUNC_BODY(Name, const Eo *, 1)
|
||||
#define EO_FUNC_PROMISE_BODYV_CONST(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, const Eo *, 1, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
#define EO_VOID_FUNC_PROMISE_BODYV_CONST(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, const Eo *, 1, EO_FUNC_CALL(Arguments), __VA_ARGS__)
|
||||
|
||||
#ifndef _WIN32
|
||||
# define _EO_OP_API_ENTRY(a) (void*)a
|
||||
#else
|
||||
|
|
|
@ -75,7 +75,8 @@ static const char * const ctypes[] =
|
|||
"Eina_Accessor", "Eina_Array", "Eina_Iterator", "Eina_Hash", "Eina_List",
|
||||
"Eina_Value",
|
||||
|
||||
"Eo_Event_Cb"
|
||||
"Eo_Event_Cb",
|
||||
"Ecore_Promise"
|
||||
};
|
||||
|
||||
#undef KW
|
||||
|
|
|
@ -52,7 +52,9 @@ enum Tokens
|
|||
\
|
||||
KW(accessor), KW(array), KW(iterator), KW(hash), KW(list), KW(generic_value), \
|
||||
\
|
||||
KW(__builtin_event_cb), KW(__undefined_type), \
|
||||
KW(__builtin_event_cb), \
|
||||
KW(Promise), \
|
||||
KW(__undefined_type), \
|
||||
\
|
||||
KW(true), KW(false), KW(null)
|
||||
|
||||
|
@ -206,4 +208,4 @@ void eo_lexer_context_pop (Eo_Lexer *ls);
|
|||
void eo_lexer_context_restore(Eo_Lexer *ls);
|
||||
void eo_lexer_context_clear (Eo_Lexer *ls);
|
||||
|
||||
#endif /* __EO_LEXER_H__ */
|
||||
#endif /* __EO_LEXER_H__ */
|
||||
|
|
|
@ -782,7 +782,7 @@ parse_type_void_base(Eo_Lexer *ls, Eina_Bool noptr)
|
|||
_fill_name(eina_stringshare_ref(ls->t.value.s), &def->full_name,
|
||||
&def->name, &def->namespaces);
|
||||
eo_lexer_get(ls);
|
||||
if (tpid >= KW_accessor && tpid <= KW_list)
|
||||
if ((tpid >= KW_accessor && tpid <= KW_list) || tpid == KW_Promise)
|
||||
{
|
||||
int bline = ls->line_number, bcol = ls->column;
|
||||
def->type = EOLIAN_TYPE_COMPLEX;
|
||||
|
|
|
@ -26,6 +26,7 @@ static const Efl_Test_Case etc[] = {
|
|||
#endif
|
||||
{ "Ecore_Input", ecore_test_ecore_input },
|
||||
{ "Ecore_File", ecore_test_ecore_file },
|
||||
{ "Ecore_Promise", ecore_test_ecore_promise },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -15,5 +15,6 @@ void ecore_test_ecore_drm(TCase *tc);
|
|||
void ecore_test_ecore_fb(TCase *tc);
|
||||
void ecore_test_ecore_input(TCase *tc);
|
||||
void ecore_test_ecore_file(TCase *tc);
|
||||
void ecore_test_ecore_promise(TCase *tc);
|
||||
|
||||
#endif /* _ECORE_SUITE_H */
|
||||
|
|
|
@ -0,0 +1,364 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <Ecore.h>
|
||||
#include "ecore_suite.h"
|
||||
#include <time.h>
|
||||
|
||||
void promised_thread(const void* data EINA_UNUSED, Ecore_Promise* promise)
|
||||
{
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_promise_value_set(promise, NULL);
|
||||
}
|
||||
|
||||
void promise_callback(void* data EINA_UNUSED, void* value EINA_UNUSED)
|
||||
{
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
|
||||
START_TEST(ecore_test_promise)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
Ecore_Promise* promise = ecore_promise_thread_run(&promised_thread, NULL, 0);
|
||||
ecore_promise_then(promise, &promise_callback, NULL);
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
void promise_error_thread(const void* data EINA_UNUSED, Ecore_Promise* promise)
|
||||
{
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_promise_error_set(promise, EINA_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
void promise_error_callback(void* data EINA_UNUSED, void* value EINA_UNUSED)
|
||||
{
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
|
||||
START_TEST(ecore_test_promise_error)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
Ecore_Promise* promise = ecore_promise_thread_run(&promise_error_thread, NULL, 0);
|
||||
ecore_promise_then(promise, &promise_error_callback, NULL);
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(ecore_test_promise_all)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
Ecore_Promise* first[2] = { ecore_promise_thread_run(&promised_thread, NULL, 0), NULL };
|
||||
Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0]));
|
||||
ecore_promise_then(promise, &promise_callback, NULL);
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
void promise_callback2(void* data, void* value EINA_UNUSED)
|
||||
{
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
if(++(*(int*)data) == 2)
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
|
||||
START_TEST(ecore_test_promise_all_then_then)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
int i = 0;
|
||||
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
Ecore_Promise* first[2] = { ecore_promise_thread_run(&promised_thread, NULL, 0), NULL };
|
||||
ecore_promise_then(first[0], &promise_callback2, &i);
|
||||
Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0]));
|
||||
ecore_promise_then(promise, &promise_callback2, &i);
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
struct sync_data
|
||||
{
|
||||
Eina_Lock lock;
|
||||
Eina_Condition cond;
|
||||
Eina_Bool var;
|
||||
};
|
||||
|
||||
void promised_exit_thread(struct sync_data* data EINA_UNUSED, Ecore_Promise* promise)
|
||||
{
|
||||
fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_promise_value_set(promise, NULL);
|
||||
eina_lock_take(&data->lock);
|
||||
data->var = EINA_TRUE;
|
||||
eina_condition_broadcast(&data->cond);
|
||||
eina_lock_release(&data->lock);
|
||||
}
|
||||
|
||||
static void _ecore_test_promise_then_after_thread_finished_main_cb()
|
||||
{
|
||||
struct sync_data data;
|
||||
data.var = EINA_FALSE;
|
||||
eina_lock_new(&data.lock);
|
||||
eina_condition_new(&data.cond, &data.lock);
|
||||
|
||||
Ecore_Promise* promise = ecore_promise_thread_run((Ecore_Promise_Thread_Cb)&promised_exit_thread, &data, 0);
|
||||
|
||||
eina_lock_take(&data.lock);
|
||||
while(!data.var)
|
||||
{
|
||||
eina_condition_wait(&data.cond);
|
||||
}
|
||||
eina_lock_release(&data.lock);
|
||||
ecore_promise_then(promise, &promise_callback, NULL);
|
||||
}
|
||||
|
||||
START_TEST(ecore_test_promise_then_after_thread_finished)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
ecore_job_add(&_ecore_test_promise_then_after_thread_finished_main_cb, NULL);
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void _ecore_test_promise_then_after_thread_finished_all_main_cb()
|
||||
{
|
||||
struct sync_data data;
|
||||
data.var = EINA_FALSE;
|
||||
eina_lock_new(&data.lock);
|
||||
eina_condition_new(&data.cond, &data.lock);
|
||||
|
||||
Ecore_Promise* first[] = {ecore_promise_thread_run((Ecore_Promise_Thread_Cb)&promised_exit_thread, &data, 0), NULL};
|
||||
Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0]));
|
||||
|
||||
eina_lock_take(&data.lock);
|
||||
while(!data.var)
|
||||
{
|
||||
eina_condition_wait(&data.cond);
|
||||
}
|
||||
eina_lock_release(&data.lock);
|
||||
ecore_promise_then(promise, &promise_callback, NULL);
|
||||
}
|
||||
|
||||
START_TEST(ecore_test_promise_then_after_thread_finished_all)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
ecore_job_add(&_ecore_test_promise_then_after_thread_finished_all_main_cb, NULL);
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
void promised_block_thread(const void* data EINA_UNUSED, Ecore_Promise* promise)
|
||||
{
|
||||
struct timespec v = {.tv_sec = 1, .tv_nsec = 0}, rem;
|
||||
if(nanosleep(&v, &rem) == -1 && errno == EINTR)
|
||||
do
|
||||
{
|
||||
v = rem;
|
||||
}
|
||||
while(nanosleep(&v, &rem) == -1 && errno == EINTR);
|
||||
|
||||
int r = 10;
|
||||
ecore_promise_value_set(promise, &r);
|
||||
}
|
||||
|
||||
static void
|
||||
_ecore_test_promise_blocking_get_quit_cb(void* data EINA_UNUSED)
|
||||
{
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
|
||||
static void
|
||||
_ecore_test_promise_blocking_get_main_cb(void* data EINA_UNUSED)
|
||||
{
|
||||
Ecore_Promise* promise = ecore_promise_thread_run(&promised_block_thread, NULL, sizeof(int));
|
||||
const void* value = ecore_promise_value_get(promise);
|
||||
ck_assert(*(int*)value == 10);
|
||||
|
||||
ecore_job_add(&_ecore_test_promise_blocking_get_quit_cb, NULL);
|
||||
}
|
||||
|
||||
START_TEST(ecore_test_promise_blocking_get)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
ecore_job_add(&_ecore_test_promise_blocking_get_main_cb, NULL);
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void
|
||||
_ecore_test_promise_blocking_get_all_value_get_cb(Ecore_Promise* promise, Ecore_Thread* thread EINA_UNUSED)
|
||||
{
|
||||
Eina_Iterator** iterator = ecore_promise_value_get(promise);
|
||||
int* v;
|
||||
ck_assert(eina_iterator_next(*iterator, (void**)&v));
|
||||
ck_assert(*v == 10);
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
|
||||
static void
|
||||
_ecore_test_promise_blocking_get_all_main_cb(void* data EINA_UNUSED)
|
||||
{
|
||||
Ecore_Promise* first[2] = {ecore_promise_thread_run(&promised_block_thread, NULL, sizeof(int)), NULL};
|
||||
Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0]));
|
||||
|
||||
ecore_thread_run((Ecore_Thread_Cb)&_ecore_test_promise_blocking_get_all_value_get_cb, NULL, NULL, promise);
|
||||
}
|
||||
|
||||
START_TEST(ecore_test_promise_blocking_get_all)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
ecore_job_add(&_ecore_test_promise_blocking_get_all_main_cb, NULL);
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void
|
||||
_ecore_test_promise_normal_lifetime_cb(void* data EINA_UNUSED, void* value EINA_UNUSED)
|
||||
{
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
|
||||
START_TEST(ecore_test_promise_normal_lifetime)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
Ecore_Promise* promise = ecore_promise_add(0);
|
||||
|
||||
ecore_promise_then(promise, &_ecore_test_promise_normal_lifetime_cb, NULL);
|
||||
ecore_promise_value_set(promise, NULL);
|
||||
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(ecore_test_promise_normal_lifetime_all)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
Ecore_Promise* first[2] = {ecore_promise_add(0), NULL};
|
||||
Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0]));
|
||||
|
||||
ecore_promise_then(promise, &_ecore_test_promise_normal_lifetime_cb, NULL);
|
||||
ecore_promise_value_set(promise, NULL);
|
||||
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static void
|
||||
_ecore_test_promise_immediate_set_lifetime_cb(void* data EINA_UNUSED, void* value EINA_UNUSED)
|
||||
{
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
|
||||
START_TEST(ecore_test_promise_immediate_set_lifetime)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
Ecore_Promise* promise = ecore_promise_add(0);
|
||||
|
||||
ecore_promise_value_set(promise, NULL);
|
||||
ecore_promise_then(promise, &_ecore_test_promise_immediate_set_lifetime_cb, NULL);
|
||||
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(ecore_test_promise_immediate_set_lifetime_all)
|
||||
{
|
||||
ecore_init();
|
||||
fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
|
||||
Ecore_Promise* first[2] = {ecore_promise_add(0), NULL};
|
||||
Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0]));
|
||||
|
||||
ecore_promise_value_set(first[0], NULL);
|
||||
ecore_promise_then(promise, &_ecore_test_promise_immediate_set_lifetime_cb, NULL);
|
||||
|
||||
ecore_main_loop_begin();
|
||||
|
||||
fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr);
|
||||
ecore_shutdown();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
void ecore_test_ecore_promise(TCase *tc EINA_UNUSED)
|
||||
{
|
||||
tcase_add_test(tc, ecore_test_promise);
|
||||
tcase_add_test(tc, ecore_test_promise_error);
|
||||
tcase_add_test(tc, ecore_test_promise_all);
|
||||
tcase_add_test(tc, ecore_test_promise_all_then_then);
|
||||
tcase_add_test(tc, ecore_test_promise_then_after_thread_finished);
|
||||
tcase_add_test(tc, ecore_test_promise_then_after_thread_finished_all);
|
||||
tcase_add_test(tc, ecore_test_promise_blocking_get);
|
||||
tcase_add_test(tc, ecore_test_promise_blocking_get_all);
|
||||
tcase_add_test(tc, ecore_test_promise_normal_lifetime);
|
||||
tcase_add_test(tc, ecore_test_promise_normal_lifetime_all);
|
||||
tcase_add_test(tc, ecore_test_promise_immediate_set_lifetime);
|
||||
tcase_add_test(tc, ecore_test_promise_immediate_set_lifetime_all);
|
||||
}
|
Loading…
Reference in New Issue