summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLauro Moura <lauromoura@expertisesolutions.com.br>2018-05-29 19:17:42 -0300
committerLauro Moura <lauromoura@expertisesolutions.com.br>2018-12-03 22:43:50 -0300
commit16a05a06a68e0de89f98bb9a7827fec5ec6f6cbe (patch)
tree5999d922704cc9a7199bc18e5c4d9df8f94d82cf
parentc45f1592e18ed2129027442e9865a12c5b8fb9f6 (diff)
ecore: Move Coroutine code to efl_loop proper.
-rw-r--r--src/lib/ecore/ecore_main.c754
-rw-r--r--src/lib/ecore/efl_loop.c180
-rw-r--r--src/lib/ecore/efl_loop.eo42
-rw-r--r--src/lib/eina/eina_promise.c3
4 files changed, 223 insertions, 756 deletions
diff --git a/src/lib/ecore/ecore_main.c b/src/lib/ecore/ecore_main.c
index 7d9ca21c44..565e7c2d01 100644
--- a/src/lib/ecore/ecore_main.c
+++ b/src/lib/ecore/ecore_main.c
@@ -2837,758 +2837,4 @@ err:
2837} 2837}
2838#endif 2838#endif
2839 2839
2840Eo *_mainloop_singleton = NULL;
2841 2840
2842EOLIAN static Efl_Loop *
2843_efl_loop_main_get(Efl_Class *klass EINA_UNUSED, void *_pd EINA_UNUSED)
2844{
2845 if (!_mainloop_singleton)
2846 {
2847 _mainloop_singleton = efl_add(EFL_LOOP_CLASS, NULL);
2848 }
2849
2850 return _mainloop_singleton;
2851}
2852
2853EAPI Eo *
2854ecore_main_loop_get(void)
2855{
2856 return efl_loop_main_get(EFL_LOOP_CLASS);
2857}
2858
2859EOLIAN static void
2860_efl_loop_iterate(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
2861{
2862 ecore_main_loop_iterate();
2863}
2864
2865EOLIAN static int _efl_loop_iterate_may_block(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED, int may_block)
2866{
2867 return ecore_main_loop_iterate_may_block(may_block);
2868}
2869
2870EOLIAN static unsigned char
2871_efl_loop_begin(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
2872{
2873 ecore_main_loop_begin();
2874 return _ecore_exit_code;
2875}
2876
2877EOLIAN static void
2878_efl_loop_quit(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED, unsigned char exit_code)
2879{
2880 ecore_main_loop_quit();
2881 _ecore_exit_code = exit_code;
2882}
2883
2884EOLIAN static Efl_Object *
2885_efl_loop_efl_object_provider_find(Eo *obj, Efl_Loop_Data *pd, const Efl_Object *klass)
2886{
2887 Efl_Object *r;
2888
2889 if (klass == EFL_LOOP_CLASS) return obj;
2890
2891 r = eina_hash_find(pd->providers, &klass);
2892 if (r) return r;
2893
2894 return efl_provider_find(efl_super(obj, EFL_LOOP_CLASS), klass);
2895}
2896
2897static void
2898_poll_trigger(void *data, const Efl_Event *event)
2899{
2900 Eo *parent = efl_parent_get(event->object);
2901
2902 efl_event_callback_call(parent, data, NULL);
2903}
2904
2905static void
2906_check_event_catcher_add(void *data, const Efl_Event *event)
2907{
2908 const Efl_Callback_Array_Item *array = event->info;
2909 Efl_Loop_Data *pd = data;
2910 int i;
2911
2912 for (i = 0; array[i].desc != NULL; i++)
2913 {
2914 if (array[i].desc == EFL_LOOP_EVENT_IDLE)
2915 {
2916 ++pd->idlers;
2917 }
2918 // XXX: all the below are kind of bad. ecore_pollers were special.
2919 // they all woke up at the SAME time based on interval, (all pollers
2920 // of interval 1 woke up together, those with 2 woke up when 1 and
2921 // 2 woke up, 4 woke up together along with 1 and 2 etc.
2922 // the below means they will just go off whenever but at a pre
2923 // defined interval - 1/60th, 6 and 66 seconds. not really great
2924 // pollers probably should be less frequent that 1/60th even on poll
2925 // high, medium probably down to 1-2 sec and low - yes maybe 30 or 60
2926 // sec... still - not timed to wake up together. :(
2927 else if (array[i].desc == EFL_LOOP_EVENT_POLL_HIGH)
2928 {
2929 if (!pd->poll_high)
2930 {
2931 // Would be better to have it in sync with normal wake up
2932 // of the main loop for better energy efficiency, I guess.
2933 pd->poll_high = efl_add(EFL_LOOP_TIMER_CLASS, event->object,
2934 efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK, _poll_trigger, EFL_LOOP_EVENT_POLL_HIGH),
2935 efl_loop_timer_interval_set(efl_added, 1.0/60.0));
2936 }
2937 ++pd->pollers.high;
2938 }
2939 else if (array[i].desc == EFL_LOOP_EVENT_POLL_MEDIUM)
2940 {
2941 if (!pd->poll_medium)
2942 {
2943 pd->poll_medium = efl_add(EFL_LOOP_TIMER_CLASS, event->object,
2944 efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK, _poll_trigger, EFL_LOOP_EVENT_POLL_MEDIUM),
2945 efl_loop_timer_interval_set(efl_added, 6));
2946 }
2947 ++pd->pollers.medium;
2948 }
2949 else if (array[i].desc == EFL_LOOP_EVENT_POLL_LOW)
2950 {
2951 if (!pd->poll_low)
2952 {
2953 pd->poll_low = efl_add(EFL_LOOP_TIMER_CLASS, event->object,
2954 efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK, _poll_trigger, EFL_LOOP_EVENT_POLL_LOW),
2955 efl_loop_timer_interval_set(efl_added, 66));
2956 }
2957 ++pd->pollers.low;
2958 }
2959 }
2960}
2961
2962static void
2963_check_event_catcher_del(void *data, const Efl_Event *event)
2964{
2965 const Efl_Callback_Array_Item *array = event->info;
2966 Efl_Loop_Data *pd = data;
2967 int i;
2968
2969 for (i = 0; array[i].desc != NULL; i++)
2970 {
2971 if (array[i].desc == EFL_LOOP_EVENT_IDLE)
2972 {
2973 --pd->idlers;
2974 }
2975 else if (array[i].desc == EFL_LOOP_EVENT_POLL_HIGH)
2976 {
2977 --pd->pollers.high;
2978 if (!pd->pollers.high)
2979 {
2980 ecore_timer_del(pd->poll_high);
2981 pd->poll_high = NULL;
2982 }
2983 }
2984 else if (array[i].desc == EFL_LOOP_EVENT_POLL_MEDIUM)
2985 {
2986 --pd->pollers.medium;
2987 if (!pd->pollers.medium)
2988 {
2989 ecore_timer_del(pd->poll_medium);
2990 pd->poll_medium = NULL;
2991 }
2992 }
2993 else if (array[i].desc == EFL_LOOP_EVENT_POLL_LOW)
2994 {
2995 --pd->pollers.low;
2996 if (!pd->pollers.low)
2997 {
2998 ecore_timer_del(pd->poll_low);
2999 pd->poll_low = NULL;
3000 }
3001 }
3002 }
3003}
3004
3005EFL_CALLBACKS_ARRAY_DEFINE(event_catcher_watch,
3006 { EFL_EVENT_CALLBACK_ADD, _check_event_catcher_add },
3007 { EFL_EVENT_CALLBACK_DEL, _check_event_catcher_del });
3008
3009EOLIAN static Efl_Object *
3010_efl_loop_efl_object_constructor(Eo *obj, Efl_Loop_Data *pd)
3011{
3012 obj = efl_constructor(efl_super(obj, EFL_LOOP_CLASS));
3013 if (!obj) return NULL;
3014
3015 efl_event_callback_array_add(obj, event_catcher_watch(), pd);
3016
3017 pd->providers = eina_hash_pointer_new((void*) efl_unref);
3018
3019 return obj;
3020}
3021
3022EOLIAN static void
3023_efl_loop_efl_object_destructor(Eo *obj, Efl_Loop_Data *pd)
3024{
3025 eina_hash_free(pd->providers);
3026
3027 efl_del(pd->poll_low);
3028 efl_del(pd->poll_medium);
3029 efl_del(pd->poll_high);
3030
3031 efl_destructor(efl_super(obj, EFL_LOOP_CLASS));
3032}
3033
3034typedef struct _Efl_Internal_Promise Efl_Internal_Promise;
3035struct _Efl_Internal_Promise
3036{
3037 union {
3038 Ecore_Job *job;
3039 Efl_Loop_Timer *timer;
3040 } u;
3041 Efl_Promise *promise;
3042
3043 const void *data;
3044
3045 Eina_Bool job_is : 1;
3046};
3047
3048static void
3049_efl_loop_job_cb(void *data)
3050{
3051 Efl_Internal_Promise *j = data;
3052
3053 efl_promise_value_set(j->promise, (void*) j->data, NULL);
3054
3055 free(j);
3056}
3057
3058static void
3059_efl_loop_arguments_cleanup(Eina_Array *arga)
3060{
3061 Eina_Stringshare *s;
3062
3063 while ((s = eina_array_pop(arga)))
3064 eina_stringshare_del(s);
3065 eina_array_free(arga);
3066}
3067
3068static void
3069_efl_loop_arguments_send(void *data, const Efl_Event *ev EINA_UNUSED)
3070{
3071 static Eina_Bool initialization = EINA_TRUE;
3072 Efl_Loop_Arguments arge;
3073 Eina_Array *arga = data;
3074
3075 arge.argv = arga;
3076 arge.initialization = initialization;
3077 initialization = EINA_FALSE;
3078
3079 efl_event_callback_call(ecore_main_loop_get(), EFL_LOOP_EVENT_ARGUMENTS, &arge);
3080
3081 _efl_loop_arguments_cleanup(arga);
3082}
3083
3084static void
3085_efl_loop_arguments_cancel(void *data, const Efl_Event *ev EINA_UNUSED)
3086{
3087 _efl_loop_arguments_cleanup(data);
3088}
3089
3090// It doesn't make sense to send those argument to any other mainloop
3091// As it also doesn't make sense to allow anyone to override this, so
3092// should be internal for sure, not even protected.
3093EAPI void
3094ecore_loop_arguments_send(int argc, const char **argv)
3095{
3096 Efl_Future *job;
3097 Eina_Array *arga;
3098 int i = 0;
3099
3100 arga = eina_array_new(argc);
3101 for (i = 0; i < argc; i++)
3102 eina_array_push(arga, eina_stringshare_add(argv[i]));
3103
3104 job = efl_loop_job(ecore_main_loop_get(), NULL);
3105 efl_future_then(job, _efl_loop_arguments_send, _efl_loop_arguments_cancel, NULL, arga);
3106}
3107
3108static void _efl_loop_timeout_force_cancel_cb(void *data, const Efl_Event *event EINA_UNUSED);
3109static void _efl_loop_timeout_cb(void *data, const Efl_Event *event EINA_UNUSED);
3110
3111// Only one main loop handle for now
3112void
3113ecore_loop_future_register(Efl_Loop *l EINA_UNUSED, Efl_Future *f)
3114{
3115 _pending_futures = eina_list_append(_pending_futures, f);
3116}
3117
3118void
3119ecore_loop_future_unregister(Efl_Loop *l EINA_UNUSED, Efl_Future *f)
3120{
3121 _pending_futures = eina_list_remove(_pending_futures, f);
3122}
3123
3124void
3125ecore_loop_promise_register(Efl_Loop *l EINA_UNUSED, Efl_Promise *p)
3126{
3127 _pending_promises = eina_list_append(_pending_promises, p);
3128}
3129
3130void
3131ecore_loop_promise_unregister(Efl_Loop *l EINA_UNUSED, Efl_Promise *p)
3132{
3133 _pending_promises = eina_list_remove(_pending_promises, p);
3134}
3135
3136EFL_CALLBACKS_ARRAY_DEFINE(timeout,
3137 { EFL_LOOP_TIMER_EVENT_TICK, _efl_loop_timeout_cb },
3138 { EFL_EVENT_DEL, _efl_loop_timeout_force_cancel_cb });
3139
3140static Eina_Future *
3141_efl_loop_Eina_FutureXXX_job(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED)
3142{
3143 // NOTE: Eolian should do efl_future_then() to bind future to object.
3144 return efl_future_Eina_FutureXXX_then(obj,
3145 eina_future_resolved(efl_loop_future_scheduler_get(obj),
3146 EINA_VALUE_EMPTY));
3147}
3148
3149static void
3150_efl_loop_Eina_FutureXXX_idle_cancel(void *data, const Eina_Promise *dead_ptr EINA_UNUSED)
3151{
3152 Efl_Loop_Promise_Simple_Data *d = data;
3153 ecore_idler_del(d->idler);
3154 efl_loop_promise_simple_data_mp_free(d);
3155}
3156
3157static Eina_Bool
3158_efl_loop_Eina_FutureXXX_idle_done(void *data)
3159{
3160 Efl_Loop_Promise_Simple_Data *d = data;
3161 eina_promise_resolve(d->promise, EINA_VALUE_EMPTY);
3162 efl_loop_promise_simple_data_mp_free(d);
3163 return EINA_FALSE;
3164}
3165
3166static Eina_Future *
3167_efl_loop_Eina_FutureXXX_idle(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED)
3168{
3169 Efl_Loop_Promise_Simple_Data *d;
3170 Eina_Promise *p;
3171
3172 d = efl_loop_promise_simple_data_calloc(1);
3173 EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL);
3174
3175 d->idler = ecore_idler_add(_efl_loop_Eina_FutureXXX_idle_done, d);
3176 EINA_SAFETY_ON_NULL_GOTO(d->idler, idler_error);
3177
3178 p = eina_promise_new(efl_loop_future_scheduler_get(obj),
3179 _efl_loop_Eina_FutureXXX_idle_cancel, d);
3180 // d is dead if p is NULL
3181 EINA_SAFETY_ON_NULL_RETURN_VAL(p, NULL);
3182 d->promise = p;
3183
3184 // NOTE: Eolian should do efl_future_then() to bind future to object.
3185 return efl_future_Eina_FutureXXX_then(obj, eina_future_new(p));
3186
3187 idler_error:
3188 efl_loop_promise_simple_data_mp_free(d);
3189 return NULL;
3190}
3191
3192static void
3193_efl_loop_Eina_FutureXXX_timeout_cancel(void *data, const Eina_Promise *dead_ptr EINA_UNUSED)
3194{
3195 Efl_Loop_Promise_Simple_Data *d = data;
3196 ecore_timer_del(d->timer);
3197 efl_loop_promise_simple_data_mp_free(d);
3198}
3199
3200static Eina_Bool
3201_efl_loop_Eina_FutureXXX_timeout_done(void *data)
3202{
3203 Efl_Loop_Promise_Simple_Data *d = data;
3204 eina_promise_resolve(d->promise, EINA_VALUE_EMPTY);
3205 efl_loop_promise_simple_data_mp_free(d);
3206 return EINA_FALSE;
3207}
3208
3209static Eina_Future *
3210_efl_loop_Eina_FutureXXX_timeout(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, double time)
3211{
3212 Efl_Loop_Promise_Simple_Data *d;
3213 Eina_Promise *p;
3214
3215 d = efl_loop_promise_simple_data_calloc(1);
3216 EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL);
3217
3218 d->timer = ecore_timer_add(time, _efl_loop_Eina_FutureXXX_timeout_done, d);
3219 EINA_SAFETY_ON_NULL_GOTO(d->timer, timer_error);
3220
3221 p = eina_promise_new(efl_loop_future_scheduler_get(obj),
3222 _efl_loop_Eina_FutureXXX_timeout_cancel, d);
3223 // d is dead if p is NULL
3224 EINA_SAFETY_ON_NULL_RETURN_VAL(p, NULL);
3225 d->promise = p;
3226
3227 // NOTE: Eolian should do efl_future_then() to bind future to object.
3228 return efl_future_Eina_FutureXXX_then(obj, eina_future_new(p));
3229
3230 timer_error:
3231 efl_loop_promise_simple_data_mp_free(d);
3232 return NULL;
3233}
3234
3235typedef struct _Efl_Loop_Coro {
3236 Eina_Promise *promise;
3237 Eina_Coro *coro;
3238 Efl_Loop *loop;
3239 Eina_Future *scheduled;
3240 Efl_Loop_Coro_Cb func;
3241 const void *func_data;
3242 Eina_Free_Cb func_free_cb;
3243 Efl_Loop_Coro_Prio prio;
3244 Eina_Value value;
3245} Efl_Loop_Coro;
3246
3247static void
3248_efl_loop_coro_free(Efl_Loop_Coro *lc)
3249{
3250 if (lc->func_free_cb) lc->func_free_cb((void *)lc->func_data);
3251 if (lc->scheduled) eina_future_cancel(lc->scheduled);
3252 eina_value_flush(&lc->value);
3253 efl_unref(lc->loop);
3254 free(lc);
3255}
3256
3257static void _efl_loop_coro_reschedule(Efl_Loop_Coro *lc);
3258
3259static Eina_Value
3260_efl_loop_coro_schedule_resolved(void *data, const Eina_Value value, const Eina_Future *dead_future EINA_UNUSED)
3261{
3262 Efl_Loop_Coro *lc = data;
3263 Eina_Future *awaiting = NULL;
3264
3265 if (value.type == EINA_VALUE_TYPE_ERROR)
3266 {
3267 Eina_Error err;
3268 eina_value_get(&value, &err);
3269 ERR("coro %p scheduled got error %s, try again.",
3270 lc, eina_error_msg_get(err));
3271 }
3272 else if (!eina_coro_run(&lc->coro, NULL, &awaiting))
3273 {
3274 INF("coroutine %p finished with value type=%p (%s)",
3275 lc, lc->value.type,
3276 lc->value.type ? lc->value.type->name : "EMPTY");
3277
3278 eina_promise_resolve(lc->promise, lc->value);
3279 lc->value = EINA_VALUE_EMPTY; // owned by promise
3280 _efl_loop_coro_free(lc);
3281 return value;
3282 }
3283 else if (awaiting)
3284 {
3285 DBG("coroutine %p is awaiting for future %p, do not reschedule", lc, awaiting);
3286 eina_future_chain(awaiting,
3287 {
3288 .cb = _efl_loop_coro_schedule_resolved,
3289 .data = lc,
3290 .storage = &lc->scheduled,
3291 },
3292 efl_future_cb(lc->loop));
3293 }
3294 else _efl_loop_coro_reschedule(lc);
3295
3296 return value;
3297}
3298
3299static void
3300_efl_loop_coro_reschedule(Efl_Loop_Coro *lc)
3301{
3302 Eina_Future *f;
3303
3304 // high uses 0-timeout instead of job, since job
3305 // is implemented using events and the Ecore implementation
3306 // will never run timers or anything else, just the new jobs :-/
3307 //
3308 // TODO: bug report ecore_main loop bug.
3309 if (lc->prio == EFL_LOOP_CORO_PRIO_HIGH)
3310 f = efl_loop_Eina_FutureXXX_timeout(lc->loop, 0);
3311 else
3312 f = efl_loop_Eina_FutureXXX_idle(lc->loop);
3313
3314 DBG("coroutine %p rescheduled as future=%p", lc, f);
3315
3316 // NOTE: efl_future_cb() doesn't allow for extra 'data', so it matches
3317 // methods more easily. However we need 'lc' and we can't store in
3318 // loop since we'd not know the key for efl_key_data_get().
3319 // Easy solution: use 2 futures, one to bind and another to resolve.
3320 eina_future_chain(f,
3321 {
3322 .cb = _efl_loop_coro_schedule_resolved,
3323 .data = lc,
3324 .storage = &lc->scheduled,
3325 },
3326 efl_future_cb(lc->loop));
3327}
3328
3329static void
3330_efl_loop_coro_cancel(void *data, const Eina_Promise *dead_promise EINA_UNUSED)
3331{
3332 Efl_Loop_Coro *lc = data;
3333
3334 INF("canceled coroutine %p (coro=%p)", lc, lc->coro);
3335
3336 eina_coro_cancel(&lc->coro);
3337
3338 _efl_loop_coro_free(lc);
3339}
3340
3341static const void *
3342_efl_loop_coro_cb(void *data, Eina_Bool canceled, Eina_Coro *coro)
3343{
3344 Efl_Loop_Coro *lc = data;
3345
3346 if (canceled) lc->value = eina_value_error_init(ECANCELED);
3347 else lc->value = lc->func((void *)lc->func_data, coro, lc->loop);
3348
3349 return lc;
3350}
3351
3352static Eina_Future *
3353_efl_loop_coro(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, Efl_Loop_Coro_Prio prio, void *func_data, Efl_Loop_Coro_Cb func, Eina_Free_Cb func_free_cb)
3354{
3355 Efl_Loop_Coro *lc;
3356 Eina_Promise *p;
3357 Eina_Future *f;
3358
3359 EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL);
3360
3361 lc = calloc(1, sizeof(Efl_Loop_Coro));
3362 EINA_SAFETY_ON_NULL_GOTO(lc, calloc_failed);
3363
3364 lc->loop = efl_ref(obj);
3365 lc->func = func;
3366 lc->func_data = func_data;
3367 lc->func_free_cb = func_free_cb;
3368 lc->prio = prio;
3369
3370 lc->coro = eina_coro_new(_efl_loop_coro_cb, lc, EINA_CORO_STACK_SIZE_DEFAULT);
3371 EINA_SAFETY_ON_NULL_GOTO(lc, coro_failed);
3372
3373 p = eina_promise_new(efl_loop_future_scheduler_get(obj),
3374 _efl_loop_coro_cancel, lc);
3375 // lc is dead if p is NULL
3376 EINA_SAFETY_ON_NULL_GOTO(p, promise_failed);
3377 lc->promise = p;
3378
3379 // must be done prior to reschedule, as it may resolve on errors
3380 // and promises without futures are simply ignored, will remain
3381 // alive.
3382 f = eina_future_new(p);
3383
3384 _efl_loop_coro_reschedule(lc);
3385
3386 INF("new coroutine %p (coro=%p)", lc, lc->coro);
3387
3388 // NOTE: Eolian should do efl_future_then() to bind future to object.
3389 return efl_future_Eina_FutureXXX_then(obj, f);
3390
3391 promise_failed:
3392 // _efl_loop_coro_cancel() was called, func was run... just return.
3393
3394 // NOTE: Eolian should do efl_future_then() to bind future to object.
3395 return efl_future_Eina_FutureXXX_then(obj,
3396 eina_future_resolved(efl_loop_future_scheduler_get(obj),
3397 eina_value_error_init(ENOMEM)));
3398
3399 coro_failed:
3400 _efl_loop_coro_free(lc);
3401
3402 calloc_failed:
3403 if (func_free_cb) func_free_cb((void *)func_data);
3404
3405 // NOTE: Eolian should do efl_future_then() to bind future to object.
3406 return efl_future_Eina_FutureXXX_then(obj,
3407 eina_future_resolved(efl_loop_future_scheduler_get(obj),
3408 eina_value_error_init(ENOMEM)));
3409}
3410
3411
3412/* This event will be triggered when the main loop is destroyed and destroy its timers along */
3413static void _efl_loop_internal_cancel(Efl_Internal_Promise *p);
3414
3415static void
3416_efl_loop_timeout_force_cancel_cb(void *data, const Efl_Event *event EINA_UNUSED)
3417{
3418 _efl_loop_internal_cancel(data);
3419}
3420
3421static void _efl_loop_job_cancel(void* data, const Efl_Event *ev EINA_UNUSED);
3422
3423static void
3424_efl_loop_timeout_cb(void *data, const Efl_Event *event EINA_UNUSED)
3425{
3426 Efl_Internal_Promise *t = data;
3427
3428 efl_promise_value_set(t->promise, (void*) t->data, NULL);
3429 efl_del(t->promise);
3430
3431 efl_event_callback_array_del(t->u.timer, timeout(), t);
3432 efl_del(t->u.timer);
3433}
3434
3435static void
3436_efl_loop_internal_cancel(Efl_Internal_Promise *p)
3437{
3438 efl_promise_failed_set(p->promise, EINA_ERROR_FUTURE_CANCEL);
3439 efl_del(p->promise);
3440 free(p);
3441}
3442
3443static void
3444_efl_loop_job_cancel(void* data, const Efl_Event *ev EINA_UNUSED)
3445{
3446 Efl_Internal_Promise *j = data;
3447
3448 if (j->job_is)
3449 {
3450 ecore_job_del(j->u.job);
3451 }
3452 else
3453 {
3454 efl_event_callback_array_del(j->u.timer, timeout(), j);
3455 efl_del(j->u.timer);
3456 }
3457
3458 _efl_loop_internal_cancel(j);
3459}
3460
3461static Efl_Internal_Promise *
3462_efl_internal_promise_new(Efl_Promise* promise, const void *data)
3463{
3464 Efl_Internal_Promise *p;
3465
3466 p = calloc(1, sizeof (Efl_Internal_Promise));
3467 if (!p) return NULL;
3468
3469 efl_event_callback_add(promise, EFL_PROMISE_EVENT_FUTURE_NONE, _efl_loop_job_cancel, p);
3470
3471 p->promise = promise;
3472 p->data = data;
3473
3474 return p;
3475}
3476
3477static Efl_Future *
3478_efl_loop_job(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, const void *data)
3479{
3480 Efl_Internal_Promise *j;
3481 Efl_Object *promise;
3482
3483 promise = efl_add(EFL_PROMISE_CLASS, obj);
3484 if (!promise) return NULL;
3485
3486 j = _efl_internal_promise_new(promise, data);
3487 if (!j) goto on_error;
3488
3489 j->job_is = EINA_TRUE;
3490 j->u.job = ecore_job_add(_efl_loop_job_cb, j);
3491 if (!j->u.job) goto on_error;
3492
3493 return efl_promise_future_get(promise);
3494
3495 on_error:
3496 efl_del(promise);
3497 free(j);
3498
3499 return NULL;
3500}
3501
3502static Efl_Future *
3503_efl_loop_timeout(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, double time, const void *data)
3504{
3505 Efl_Internal_Promise *t;
3506 Efl_Object *promise;
3507
3508 promise = efl_add(EFL_PROMISE_CLASS, obj);
3509 if (!promise) return NULL;
3510
3511 t = _efl_internal_promise_new(promise, data);
3512 if (!t) goto on_error;
3513
3514 t->job_is = EINA_FALSE;
3515 t->u.timer = efl_add(EFL_LOOP_TIMER_CLASS, obj,
3516 efl_loop_timer_interval_set(efl_added, time),
3517 efl_event_callback_array_add(efl_added, timeout(), t));
3518
3519 if (!t->u.timer) goto on_error;
3520
3521 return efl_promise_future_get(promise);
3522
3523 on_error:
3524 efl_del(promise);
3525 free(t);
3526
3527 return NULL;
3528}
3529
3530static Eina_Bool
3531_efl_loop_register(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, const Efl_Class *klass, const Efl_Object *provider)
3532{
3533 // The passed object does not provide that said class.
3534 if (!efl_isa(provider, klass)) return EINA_FALSE;
3535
3536 // Note: I would prefer to use efl_xref here, but I can't figure a nice way to
3537 // call efl_xunref on hash destruction.
3538 return eina_hash_add(pd->providers, &klass, efl_ref(provider));
3539}
3540
3541static Eina_Bool
3542_efl_loop_unregister(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, const Efl_Class *klass, const Efl_Object *provider)
3543{
3544 return eina_hash_del(pd->providers, &klass, provider);
3545}
3546
3547Efl_Version _app_efl_version = { 0, 0, 0, 0, NULL, NULL };
3548
3549EWAPI void
3550efl_build_version_set(int vmaj, int vmin, int vmic, int revision,
3551 const char *flavor, const char *build_id)
3552{
3553 // note: EFL has not been initialized yet at this point (ie. no eina call)
3554 _app_efl_version.major = vmaj;
3555 _app_efl_version.minor = vmin;
3556 _app_efl_version.micro = vmic;
3557 _app_efl_version.revision = revision;
3558 free((char *) _app_efl_version.flavor);
3559 free((char *) _app_efl_version.build_id);
3560 _app_efl_version.flavor = flavor ? strdup(flavor) : NULL;
3561 _app_efl_version.build_id = build_id ? strdup(build_id) : NULL;
3562}
3563
3564EOLIAN static const Efl_Version *
3565_efl_loop_app_efl_version_get(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
3566{
3567 return &_app_efl_version;
3568}
3569
3570EOLIAN static const Efl_Version *
3571_efl_loop_efl_version_get(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
3572{
3573 /* vanilla EFL: flavor = NULL */
3574 static const Efl_Version version = {
3575 .major = VMAJ,
3576 .minor = VMIN,
3577 .micro = VMIC,
3578 .revision = VREV,
3579 .build_id = EFL_BUILD_ID,
3580 .flavor = NULL
3581 };
3582
3583 return &version;
3584}
3585
3586EOLIAN static Eina_Future_Scheduler *
3587_efl_loop_future_scheduler_get(Eo *obj EINA_UNUSED,
3588 Efl_Loop_Data *pd EINA_UNUSED)
3589{
3590 return _ecore_event_future_scheduler_get();
3591}
3592
3593
3594#include "efl_loop.eo.c"
diff --git a/src/lib/ecore/efl_loop.c b/src/lib/ecore/efl_loop.c
index 5d24608af3..ebaf157b46 100644
--- a/src/lib/ecore/efl_loop.c
+++ b/src/lib/ecore/efl_loop.c
@@ -914,4 +914,184 @@ efl_loop_promise_new(const Eo *obj, Eina_Promise_Cancel_Cb cancel_cb, const void
914 cancel_cb, data); 914 cancel_cb, data);
915} 915}
916 916
917typedef struct _Efl_Loop_Coro {
918 Eina_Promise *promise;
919 Eina_Coro *coro;
920 Efl_Loop *loop;
921 Eina_Future *scheduled;
922 Efl_Loop_Coro_Cb func;
923 const void *func_data;
924 Eina_Free_Cb func_free_cb;
925 Efl_Loop_Coro_Prio prio;
926 Eina_Value value;
927} Efl_Loop_Coro;
928
929static void
930_efl_loop_coro_free(Efl_Loop_Coro *lc)
931{
932 if (lc->func_free_cb) lc->func_free_cb((void *)lc->func_data);
933 if (lc->scheduled) eina_future_cancel(lc->scheduled);
934 eina_value_flush(&lc->value);
935 efl_unref(lc->loop);
936 free(lc);
937}
938
939static void _efl_loop_coro_reschedule(Efl_Loop_Coro *lc);
940
941static Eina_Value
942_efl_loop_coro_schedule_resolved(void *data, const Eina_Value value, const Eina_Future *dead_future EINA_UNUSED)
943{
944 Efl_Loop_Coro *lc = data;
945 Eina_Future *awaiting = NULL;
946
947 if (value.type == EINA_VALUE_TYPE_ERROR)
948 {
949 Eina_Error err;
950 eina_value_get(&value, &err);
951 ERR("coro %p scheduled got error %s, try again.",
952 lc, eina_error_msg_get(err));
953 }
954 else if (!eina_coro_run(&lc->coro, NULL, &awaiting))
955 {
956 INF("coroutine %p finished with value type=%p (%s)",
957 lc, lc->value.type,
958 lc->value.type ? lc->value.type->name : "EMPTY");
959
960 eina_promise_resolve(lc->promise, lc->value);
961 lc->value = EINA_VALUE_EMPTY; // owned by promise
962 _efl_loop_coro_free(lc);
963 return value;
964 }
965 else if (awaiting)
966 {
967 DBG("coroutine %p is awaiting for future %p, do not reschedule", lc, awaiting);
968 eina_future_chain(awaiting,
969 {
970 .cb = _efl_loop_coro_schedule_resolved,
971 .data = lc,
972 .storage = &lc->scheduled,
973 },
974 efl_future_cb(lc->loop));
975 }
976 else _efl_loop_coro_reschedule(lc);
977
978 return value;
979}
980
981static void
982_efl_loop_coro_reschedule(Efl_Loop_Coro *lc)
983{
984 Eina_Future *f;
985
986 // high uses 0-timeout instead of job, since job
987 // is implemented using events and the Ecore implementation
988 // will never run timers or anything else, just the new jobs :-/
989 //
990 // TODO: bug report ecore_main loop bug.
991 if (lc->prio == EFL_LOOP_CORO_PRIO_HIGH)
992 f = efl_loop_timeout(lc->loop, 0);
993 else
994 f = efl_loop_idle(lc->loop);
995
996 DBG("coroutine %p rescheduled as future=%p", lc, f);
997
998 // NOTE: efl_future_cb() doesn't allow for extra 'data', so it matches
999 // methods more easily. However we need 'lc' and we can't store in
1000 // loop since we'd not know the key for efl_key_data_get().
1001 // Easy solution: use 2 futures, one to bind and another to resolve.
1002 eina_future_chain(f,
1003 {
1004 .cb = _efl_loop_coro_schedule_resolved,
1005 .data = lc,
1006 .storage = &lc->scheduled,
1007 },
1008 efl_future_cb(lc->loop));
1009}
1010
1011static void
1012_efl_loop_coro_cancel(void *data, const Eina_Promise *dead_promise EINA_UNUSED)
1013{
1014 Efl_Loop_Coro *lc = data;
1015
1016 INF("canceled coroutine %p (coro=%p)", lc, lc->coro);
1017
1018 eina_coro_cancel(&lc->coro);
1019
1020 _efl_loop_coro_free(lc);
1021}
1022
1023static const void *
1024_efl_loop_coro_cb(void *data, Eina_Bool canceled, Eina_Coro *coro)
1025{
1026 Efl_Loop_Coro *lc = data;
1027
1028 if (canceled) lc->value = eina_value_error_init(ECANCELED);
1029 else lc->value = lc->func((void *)lc->func_data, coro, lc->loop);
1030
1031 return lc;
1032}
1033
1034static Eina_Future *
1035_efl_loop_coro(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, Efl_Loop_Coro_Prio prio, void *func_data, Efl_Loop_Coro_Cb func, Eina_Free_Cb func_free_cb)
1036{
1037 Efl_Loop_Coro *lc;
1038 Eina_Promise *p;
1039 Eina_Future *f;
1040
1041 EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL);
1042
1043 lc = calloc(1, sizeof(Efl_Loop_Coro));
1044 EINA_SAFETY_ON_NULL_GOTO(lc, calloc_failed);
1045
1046 lc->loop = efl_ref(obj);
1047 lc->func = func;
1048 lc->func_data = func_data;
1049 lc->func_free_cb = func_free_cb;
1050 lc->prio = prio;
1051
1052 lc->coro = eina_coro_new(_efl_loop_coro_cb, lc, EINA_CORO_STACK_SIZE_DEFAULT);
1053 EINA_SAFETY_ON_NULL_GOTO(lc, coro_failed);
1054
1055 p = eina_promise_new(efl_loop_future_scheduler_get(obj),
1056 _efl_loop_coro_cancel, lc);
1057 // lc is dead if p is NULL
1058 EINA_SAFETY_ON_NULL_GOTO(p, promise_failed);
1059 lc->promise = p;
1060
1061 // must be done prior to reschedule, as it may resolve on errors
1062 // and promises without futures are simply ignored, will remain
1063 // alive.
1064 f = eina_future_new(p);
1065
1066 _efl_loop_coro_reschedule(lc);
1067
1068 INF("new coroutine %p (coro=%p)", lc, lc->coro);
1069
1070 // NOTE: Eolian should do efl_future_then() to bind future to object.
1071 return efl_future_then(obj, f);
1072
1073 promise_failed:
1074 // _efl_loop_coro_cancel() was called, func was run... just return.
1075
1076 // NOTE: Eolian should do efl_future_then() to bind future to object.
1077 return efl_future_then(obj,
1078 eina_future_resolved(efl_loop_future_scheduler_get(obj),
1079 eina_value_error_init(ENOMEM)));
1080 /* return eina_future_resolved(efl_loop_future_scheduler_get(obj), */
1081 /* eina_value_error_init(ENOMEM)); */
1082
1083 coro_failed:
1084 _efl_loop_coro_free(lc);
1085
1086 calloc_failed:
1087 if (func_free_cb) func_free_cb((void *)func_data);
1088
1089 // NOTE: Eolian should do efl_future_then() to bind future to object.
1090 return efl_future_then(obj,
1091 eina_future_resolved(efl_loop_future_scheduler_get(obj),
1092 eina_value_error_init(ENOMEM)));
1093 /* return eina_future_resolved(efl_loop_future_scheduler_get(obj), */
1094 /* eina_value_error_init(ENOMEM)); */
1095}
1096
917#include "efl_loop.eo.c" 1097#include "efl_loop.eo.c"
diff --git a/src/lib/ecore/efl_loop.eo b/src/lib/ecore/efl_loop.eo
index 4a2b31deda..f1022e714a 100644
--- a/src/lib/ecore/efl_loop.eo
+++ b/src/lib/ecore/efl_loop.eo
@@ -3,6 +3,46 @@ struct Efl.Loop_Arguments {
3 argv: const(array<const(stringshare)>); [[Array with loop arguments]] 3 argv: const(array<const(stringshare)>); [[Array with loop arguments]]
4 initialization: bool; [[Set to $true when the program should initialize its internal state. This happen once per process instance.]] 4 initialization: bool; [[Set to $true when the program should initialize its internal state. This happen once per process instance.]]
5} 5}
6enum Efl.Loop.Coro.Prio {
7 [[Priority class for the coroutine.]]
8
9 high = 0, [[high priority coroutine, scheduled using zero-timers (will expire as soon as possible).]]
10 idle, [[low priority coroutine, scheduled when nothing else should run]]
11
12}
13
14
15function Efl.Loop.Coro.Cb {
16 [[Coroutine function
17
18 It will be called back from the
19 coroutine environment and when executed it's guaranteed that the
20 main loop will be paused, so shared resources are safe to access
21 (no locks are required).
22
23 \@note Eina_Coro may use threads, then take care to handle
24 thread-local-storage (TLS) details properly, eventually you
25 may consider eina_coro_hook_add() to be informed when the
26 main or coroutine will exit and enter. For instance this is
27 used by Efl_Object (handled transparently for the user).
28 ]]
29
30 params {
31 coro: ptr(Eina.Coro); [[The coroutine handle, used to $eina_coro_yield() and voluntarily give back control to the main loop until it's rescheduled.]]
32 loop: Efl.Loop; [[The loop that schedules this coroutine.]]
33 }
34 return: any_value; [[Value that will resolve the promise,
35 being delivered to the future chain
36 attached to the coroutine. Note that the
37 value will be owned by the Efl_Loop_Coro
38 and Eina_Future subsystems and will be
39 flushed (eina_value_flush()) once
40 unused. Its contents must survive the
41 function return, that is, it shouldn't
42 keep pointers to the stack.
43 ]]
44};
45
6 46
7class Efl.Loop extends Efl.Task 47class Efl.Loop extends Efl.Task
8{ 48{
@@ -101,7 +141,7 @@ class Efl.Loop extends Efl.Task
101 @in priority: Efl.Loop.Coro.Prio; [[The priority used to schedule the coroutine.]] 141 @in priority: Efl.Loop.Coro.Prio; [[The priority used to schedule the coroutine.]]
102 @in func: Efl.Loop.Coro.Cb @nonull; [[The function to run as a coroutine.]] 142 @in func: Efl.Loop.Coro.Cb @nonull; [[The function to run as a coroutine.]]
103 } 143 }
104 return: future<void> @owned; /* future<> */; [[The future handle, it provides the value returned by $func once it exits.]] 144 return: future<void> @owned; [[The future handle, it provides the value returned by $func once it exits.]]
105 } 145 }
106 timeout { 146 timeout {
107 [[A future promise that will be resolved from a clean main 147 [[A future promise that will be resolved from a clean main
diff --git a/src/lib/eina/eina_promise.c b/src/lib/eina/eina_promise.c
index 48f9c2ad7c..dc134574b1 100644
--- a/src/lib/eina/eina_promise.c
+++ b/src/lib/eina/eina_promise.c
@@ -1506,7 +1506,8 @@ eina_future_await(Eina_Future *f, Eina_Coro *coro, const Eina_Value_Type *succes
1506 value.type, value.type ? value.type->name : "EMPTY"); 1506 value.type, value.type ? value.type->name : "EMPTY");
1507 1507
1508 eina_value_flush(&value); 1508 eina_value_flush(&value);
1509 value = eina_value_error_init(EINVAL); 1509 eina_value_setup(&value, EINA_VALUE_TYPE_ERROR);
1510 eina_value_set(&value, EINVAL);
1510 } 1511 }
1511 1512
1512 log: 1513 log: