efl task - change exit method to use normal event for multiple listeners

we couldn't have multilpe listeners before. now we can. better this
way. have to do this now because i can't mark efl task as @beta
without taking out massive wads of efl with it.
This commit is contained in:
Carsten Haitzler 2019-09-10 23:01:19 +01:00
parent ad48272ffe
commit d1890f5eca
6 changed files with 52 additions and 145 deletions

View File

@ -5,8 +5,8 @@
#include <Eo.h>
#include <Efl_Core.h>
static void _read_change(void *data EINA_UNUSED, const Efl_Event *ev);
static Eina_Value _task_exit(void *data, Eina_Value v, const Eina_Future *dead EINA_UNUSED);
static void _read_change(void *data EINA_UNUSED, const Efl_Event *ev);
static void _task_exit(void *data EINA_UNUSED, const Efl_Event *ev);
static void
_read_change(void *data EINA_UNUSED, const Efl_Event *ev)
@ -27,16 +27,19 @@ _read_change(void *data EINA_UNUSED, const Efl_Event *ev)
}
}
static Eina_Value
_task_exit(void *data, Eina_Value v, const Eina_Future *dead EINA_UNUSED)
static void
_task_exit(void *data EINA_UNUSED, const Efl_Event *ev)
{
// called when the task says it has completed and exited.
// all output to read has stopped
Eo *obj = data;
Eo *obj = ev->object;
printf("--- [%p] EXITED exit_code=%i\n", obj, efl_task_exit_code_get(obj));
efl_loop_quit(efl_provider_find(obj, EFL_LOOP_CLASS), eina_value_int_init(99));
efl_del(obj);
return v;
// exe auto deleted at this point like efl threads. more convenient as
// you don't need to remember to delete them yourself if launching
// lots of commands - this is how ecore_exe worked. so listen to the
// exit event (or del event) if you care about this... or ref it to keep
// it around longer.
}
EAPI_MAIN void
@ -55,7 +58,8 @@ efl_main(void *data EINA_UNUSED, const Efl_Event *ev)
efl_exe_env_set(efl_added, env),
efl_task_flags_set(efl_added, EFL_TASK_FLAGS_USE_STDOUT | EFL_TASK_FLAGS_USE_STDIN),
efl_event_callback_add(efl_added, EFL_IO_READER_EVENT_CAN_READ_CHANGED, _read_change, NULL),
eina_future_then(efl_task_run(efl_added), _task_exit, efl_added)
efl_event_callback_add(efl_added, EFL_TASK_EVENT_EXIT, _task_exit, NULL),
efl_task_run(efl_added)
);
efl_unref(env);

View File

@ -8,7 +8,7 @@
static void _th_read_change(void *data EINA_UNUSED, const Efl_Event *ev);
static void _th_main(void *data EINA_UNUSED, const Efl_Event *ev);
static void _read_change(void *data EINA_UNUSED, const Efl_Event *ev);
static Eina_Value _task_exit(void *data, Eina_Value v, const Eina_Future *dead EINA_UNUSED);
static void _task_exit(void *data EINA_UNUSED, const Efl_Event *ev);
////////////////////////////////////////////////////////////////////////////
//// thread side of code
@ -85,7 +85,8 @@ _th_main(void *data EINA_UNUSED, const Efl_Event *ev)
efl_task_flags_set(efl_added, EFL_TASK_FLAGS_USE_STDOUT | EFL_TASK_FLAGS_USE_STDIN | EFL_TASK_FLAGS_EXIT_WITH_PARENT),
efl_event_callback_add(efl_added, EFL_LOOP_EVENT_ARGUMENTS, _th_main, NULL),
efl_event_callback_add(efl_added, EFL_IO_READER_EVENT_CAN_READ_CHANGED, _read_change, NULL),
eina_future_then(efl_task_run(efl_added), _task_exit, efl_added)
efl_event_callback_add(efl_added, EFL_TASK_EVENT_EXIT, _task_exit, NULL),
efl_task_run(efl_added)
);
char *buf2 = "hello-out-there2 ";
@ -117,18 +118,17 @@ _read_change(void *data EINA_UNUSED, const Efl_Event *ev)
}
}
static Eina_Value
_task_exit(void *data, Eina_Value v, const Eina_Future *dead EINA_UNUSED)
static void
_task_exit(void *data EINA_UNUSED, const Efl_Event *ev)
{
// called when the task says it has completed and exited.
// all output to read has stopped
Eo *obj = data;
Eo *obj = ev->object;
printf("--- [%p] EXITED exit_code=%i outdata=%p\n", obj, efl_task_exit_code_get(obj), efl_threadio_outdata_get(obj));
// thread object will be automatically deleted after as long as
// EFL_TASK_FLAGS_EXIT_WITH_PAREN is set on task flags, and this is
// EFL_TASK_FLAGS_EXIT_WITH_PARENT is set on task flags, and this is
// actually the default unless you change the flags to be something
// else. if you don't use this then the task/thread becomes orphaned
return v;
}
////////////////////////////////////////////////////////////////////////////
@ -181,7 +181,8 @@ efl_main(void *data EINA_UNUSED, const Efl_Event *ev)
efl_task_flags_set(efl_added, EFL_TASK_FLAGS_USE_STDOUT | EFL_TASK_FLAGS_USE_STDIN | EFL_TASK_FLAGS_EXIT_WITH_PARENT),
efl_event_callback_add(efl_added, EFL_LOOP_EVENT_ARGUMENTS, _th_main, NULL),
efl_event_callback_add(efl_added, EFL_IO_READER_EVENT_CAN_READ_CHANGED, _read_change, NULL),
eina_future_then(efl_task_run(efl_added), _task_exit, efl_added)
efl_event_callback_add(efl_added, EFL_TASK_EVENT_EXIT, _task_exit, NULL),
efl_task_run(efl_added)
);
char *buf2 = "hello-out-there ";

View File

@ -56,7 +56,6 @@ struct _Efl_Exe_Data
Eina_Bool can_write : 1;
} fd;
#else
Eina_Promise *promise;
Eo *exit_handler;
pid_t pid;
struct {
@ -178,47 +177,8 @@ _exe_exit_eval(Eo *obj, Efl_Exe_Data *pd)
(pd->fd.exited_read == -1) && (!pd->exit_called))
{
pd->exit_called = EINA_TRUE;
if (pd->promise)
{
Eina_Promise *p = pd->promise;
int exit_code = efl_task_exit_code_get(obj);
if ((exit_code != 0) && (!(efl_task_flags_get(obj) &
EFL_TASK_FLAGS_NO_EXIT_CODE_ERROR)))
{
Eina_Error err = exit_code + 1000000;
// Code Meaning Example Comments
// ---------------------------------------------------------------------------------------------------------------------------------------------------------------------
// 1 Catchall for general errors let "var1 = 1/0" Miscellaneous errors, such as "divide by zero" and other impermissible operations
// 2 Misuse of shell builtins empty_function() {} Missing keyword or command, or permission problem (and diff return code on a failed binary file comparison).
// 126 Command invoked cannot execute /dev/null Permission problem or command is not an executable
// 127 "command not found" illegal_command Possible problem with $PATH or a typo
// 128 Invalid argument to exit exit 3.14159 exit takes only integer args in the range 0 - 255 (see first footnote)
// 128+n Fatal error signal "n" kill -9 $PPID $? returns 137 (128 + 9)
// 130 Script terminated by Control-C Ctl-C Control-C is fatal error signal 2, (130 = 128 + 2, see above)
// 255* Exit status out of range exit -1 exit takes only integer args in the range 0 - 255
//
// According to the above table, exit codes 1 - 2,
// 126 - 165, and 255 [1] have special meanings, and
// should therefore be avoided for user-specified exit
// parameters. Ending a script with exit 127 would
// certainly cause confusion when troubleshooting (is
// the error code a "command not found" or a user-defined
// one?). However, many scripts use an exit 1 as a general
// bailout-upon-error. Since exit code 1 signifies so many
// possible errors, it is not particularly useful in
// debugging.
if (exit_code == 1 ) err = EBADF;
else if (exit_code == 2 ) err = EDOM;
else if (exit_code == 126) err = ENOEXEC;
else if (exit_code == 127) err = ENOENT;
else if (exit_code == 128) err = EINVAL;
else if (exit_code == 129) err = EFAULT;
else if (exit_code == 130) err = EINTR;
else if ((exit_code >= 131) && (exit_code <= 165)) err = EFAULT;
eina_promise_reject(p, err);
}
else eina_promise_resolve(p, eina_value_int_init(exit_code));
}
efl_event_callback_call(obj, EFL_TASK_EVENT_EXIT, NULL);
efl_del(obj);
}
}
@ -268,24 +228,6 @@ _cb_exe_in(void *data, const Efl_Event *event EINA_UNUSED)
Eo *obj = data;
efl_io_writer_can_write_set(obj, EINA_TRUE);
}
static Eina_Value
_run_cancel_cb(Efl_Loop_Consumer *consumer, void *data EINA_UNUSED, Eina_Error error)
{
if (error == ECANCELED) efl_task_end(consumer);
return eina_value_error_init(error);
}
static void
_run_clean_cb(Efl_Loop_Consumer *consumer EINA_UNUSED,
void *data,
const Eina_Future *dead_future EINA_UNUSED)
{
Efl_Exe_Data *pd = data;
pd->promise = NULL;
}
#endif
//////////////////////////////////////////////////////////////////////////
@ -399,7 +341,7 @@ _efl_exe_efl_task_priority_get(const Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
return pri;
}
EOLIAN static Eina_Future *
EOLIAN static Eina_Bool
_efl_exe_efl_task_run(Eo *obj, Efl_Exe_Data *pd)
{
#ifdef _WIN32
@ -414,20 +356,20 @@ _efl_exe_efl_task_run(Eo *obj, Efl_Exe_Data *pd)
int pipe_exited[2];
int ret;
if (pd->run) return NULL;
if (pd->pid != -1) return NULL;
if (!td) return NULL;
if (pd->run) return EINA_FALSE;
if (pd->pid != -1) return EINA_FALSE;
if (!td) return EINA_FALSE;
// get a cmdline to run
cmd = efl_core_command_line_command_get(obj);
if (!cmd) return NULL;
if (!cmd) return EINA_FALSE;
ret = pipe(pipe_exited);
if (EINA_UNLIKELY(ret != 0))
{
const int error = errno;
ERR("pipe() failed: %s", strerror(error));
return NULL;
return EINA_FALSE;
}
pd->fd.exited_read = pipe_exited[0];
@ -442,7 +384,7 @@ _efl_exe_efl_task_run(Eo *obj, Efl_Exe_Data *pd)
{
const int error = errno;
ERR("pipe() failed: %s", strerror(error));
return NULL;
return EINA_FALSE;
}
pd->fd.in = pipe_stdin[1];
if (fcntl(pd->fd.in, F_SETFL, O_NONBLOCK) < 0)
@ -461,7 +403,7 @@ _efl_exe_efl_task_run(Eo *obj, Efl_Exe_Data *pd)
{
const int error = errno;
ERR("pipe() failed: %s", strerror(error));
return NULL;
return EINA_FALSE;
}
pd->fd.out = pipe_stdout[0];
if (fcntl(pd->fd.out, F_SETFL, O_NONBLOCK) < 0)
@ -488,7 +430,7 @@ _efl_exe_efl_task_run(Eo *obj, Efl_Exe_Data *pd)
{
_close_fds(pd);
_ecore_signal_pid_unlock();
return NULL;
return EINA_FALSE;
}
// register this pid in the core sigchild/pid exit code watcher
_ecore_signal_pid_register(pd->pid, pd->fd.exited_write);
@ -502,11 +444,7 @@ _efl_exe_efl_task_run(Eo *obj, Efl_Exe_Data *pd)
EFL_LOOP_HANDLER_FLAGS_READ));
_ecore_signal_pid_unlock();
pd->run = EINA_TRUE;
pd->promise = efl_loop_promise_new(obj);
return efl_future_then(obj, eina_future_new(pd->promise),
.data = pd,
.error = _run_cancel_cb,
.free = _run_clean_cb);
return EINA_TRUE;
}
// this code is in the child here, and is temporary setup until we
// exec() the child to replace everything.
@ -609,7 +547,7 @@ _efl_exe_efl_task_run(Eo *obj, Efl_Exe_Data *pd)
(errno == ENOEXEC) || (errno == ENOMEM))
exit(126);
exit(127);
return NULL;
return EINA_FALSE;
#endif
}
@ -651,7 +589,7 @@ _efl_exe_efl_object_destructor(Eo *obj, Efl_Exe_Data *pd)
{
#ifdef _WIN32
#else
if (pd->promise)
if (!pd->exit_called)
ERR("Exe being destroyed while child has not exited yet.");
if (pd->fd.exited_read >= 0)
{

View File

@ -642,21 +642,11 @@ efl_build_version_set(int vmaj, int vmin, int vmic, int revision,
_app_efl_version.build_id = build_id ? strdup(build_id) : NULL;
}
EOLIAN static Eina_Future *
EOLIAN static Eina_Bool
_efl_loop_efl_task_run(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED)
{
Eina_Value *ret;
int real;
ret = efl_loop_begin(obj);
real = efl_loop_exit_code_process(ret);
if (real == 0)
{
// we never return a valid future here because there is no loop
// any more to process the future callback as we would have quit
return NULL;
}
return NULL;
efl_loop_exit_code_process(efl_loop_begin(obj));
return EINA_TRUE;
}
EOLIAN static void

View File

@ -54,7 +54,7 @@ abstract Efl.Task extends Efl.Loop_Consumer
}
run @pure_virtual {
[[Actually run the task.]]
return: future<void> @move; [[A future triggered when task exits and is passed int exit code.]]
return: bool; [[On success in starting the task, return true, otherwise false]]
}
end @pure_virtual {
[[Request the task end (may send a signal or interrupt
@ -63,6 +63,7 @@ abstract Efl.Task extends Efl.Loop_Consumer
}
}
events {
exit: void; [[Called when the task exits. You can pick up any information you need at this point such as exit_code etc.]]
}
implements {
Efl.Object.constructor;

View File

@ -68,7 +68,6 @@ struct _Efl_Thread_Data
Eina_Bool can_write : 1;
} fd, ctrl;
int read_listeners;
Eina_Promise *promise;
Eo *loop;
Thread_Data *thdat;
Efl_Callback_Array_Item_Full *event_cb;
@ -363,15 +362,7 @@ _thread_exit_eval(Eo *obj, Efl_Thread_Data *pd)
{
pd->exit_called = EINA_TRUE;
if (pd->thdat) efl_threadio_outdata_set(obj, pd->thdat->outdata);
if (pd->promise)
{
Eina_Promise *p = pd->promise;
int exit_code = efl_task_exit_code_get(obj);
if ((exit_code != 0) && (!(efl_task_flags_get(obj) &
EFL_TASK_FLAGS_NO_EXIT_CODE_ERROR)))
eina_promise_reject(p, exit_code + 1000000);
else eina_promise_resolve(p, eina_value_int_init(exit_code));
}
efl_event_callback_call(obj, EFL_TASK_EVENT_EXIT, NULL);
efl_del(obj);
}
}
@ -448,22 +439,6 @@ _cb_thread_parent_ctrl_out(void *data, const Efl_Event *event EINA_UNUSED)
//////////////////////////////////////////////////////////////////////////
static Eina_Value
_run_cancel_cb(Efl_Loop_Consumer *consumer, void *data EINA_UNUSED, Eina_Error error)
{
if (error == ECANCELED) efl_task_end(consumer);
return eina_value_error_init(error);
}
static void
_run_clean_cb(Efl_Loop_Consumer *consumer EINA_UNUSED,void *data, const Eina_Future *dead_future EINA_UNUSED)
{
Efl_Thread_Data *pd = data;
pd->promise = NULL;
}
static void
_thread_parent_read_listeners_modify(Efl_Thread_Data *pd, int mod)
{
@ -613,7 +588,7 @@ _efl_thread_efl_object_finalize(Eo *obj, Efl_Thread_Data *pd EINA_UNUSED)
EOLIAN static void
_efl_thread_efl_object_destructor(Eo *obj, Efl_Thread_Data *pd)
{
if (pd->promise)
if (pd->exit_called)
ERR("Thread being destroyed while real worker has not exited yet.");
if (pd->thdat)
{
@ -664,7 +639,7 @@ _task_run_pipe_fail_clear(Thread_Data *thdat, Efl_Thread_Data *pd)
free(thdat);
}
EOLIAN static Eina_Future *
EOLIAN static Eina_Bool
_efl_thread_efl_task_run(Eo *obj, Efl_Thread_Data *pd)
{
Eina_Thread_Priority pri;
@ -676,10 +651,10 @@ _efl_thread_efl_task_run(Eo *obj, Efl_Thread_Data *pd)
Efl_Callback_Array_Item_Full *it;
Efl_Task_Data *td = efl_data_scope_get(obj, EFL_TASK_CLASS);
if (pd->run) return NULL;
if (!td) return NULL;
if (pd->run) return EINA_FALSE;
if (!td) return EINA_FALSE;
thdat = calloc(1, sizeof(Thread_Data));
if (!thdat) return NULL;
if (!thdat) return EINA_FALSE;
thdat->fd.in = -1;
thdat->fd.out = -1;
thdat->ctrl.in = -1;
@ -695,7 +670,7 @@ _efl_thread_efl_task_run(Eo *obj, Efl_Thread_Data *pd)
{
ERR("Can't create to_thread pipe");
free(thdat);
return NULL;
return EINA_FALSE;
}
}
if (td->flags & EFL_TASK_FLAGS_USE_STDOUT)
@ -709,7 +684,7 @@ _efl_thread_efl_task_run(Eo *obj, Efl_Thread_Data *pd)
close(pipe_from_thread[1]);
}
free(thdat);
return NULL;
return EINA_FALSE;
}
}
if (td->flags & EFL_TASK_FLAGS_USE_STDIN)
@ -752,7 +727,7 @@ _efl_thread_efl_task_run(Eo *obj, Efl_Thread_Data *pd)
{
ERR("Can't create to_thread control pipe");
_task_run_pipe_fail_clear(thdat, pd);
return NULL;
return EINA_FALSE;
}
if (pipe(pipe_from_thread) != 0)
{
@ -760,7 +735,7 @@ _efl_thread_efl_task_run(Eo *obj, Efl_Thread_Data *pd)
_task_run_pipe_fail_clear(thdat, pd);
close(pipe_to_thread[0]);
close(pipe_to_thread[1]);
return NULL;
return EINA_FALSE;
}
thdat->ctrl.in = pipe_from_thread[1]; // write - input to parent
thdat->ctrl.out = pipe_to_thread [0]; // read - output from parent
@ -864,13 +839,11 @@ _efl_thread_efl_task_run(Eo *obj, Efl_Thread_Data *pd)
pd->fd.out = -1;
pd->ctrl.in = -1;
pd->ctrl.out = -1;
return NULL;
return EINA_FALSE;
}
pd->thdat = thdat;
pd->run = EINA_TRUE;
pd->promise = efl_loop_promise_new(obj);
return efl_future_then(obj, eina_future_new(pd->promise),
.data = pd, .error = _run_cancel_cb, .free = _run_clean_cb);
return EINA_TRUE;
}
EOLIAN static void