efl loops/threads - by defaylt tasks (exe and threads) exit with parent

this also defers parent exit until all children exit and will wait
around looping until those children do report back with exited status
etc. - this meay mean some hangs for badly written/blocking apps that
have efl thrrads that refuse to exit. a slight policy change also
means that by default thread objects also get auto-deleted whent hey
report back exit codes etc. which leads to less code if you don't care
about this.
This commit is contained in:
Carsten Haitzler 2019-08-26 13:46:32 +01:00
parent 634ebfeaf1
commit d4c123d360
9 changed files with 94 additions and 10 deletions

View File

@ -124,7 +124,10 @@ _task_exit(void *data, Eina_Value v, const Eina_Future *dead EINA_UNUSED)
// all output to read has stopped
Eo *obj = data;
printf("--- [%p] EXITED exit_code=%i outdata=%p\n", obj, efl_task_exit_code_get(obj), efl_threadio_outdata_get(obj));
efl_del(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
// 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;
}

View File

@ -144,6 +144,8 @@ struct _Efl_Loop_Data
Eina_List *win32_handlers_to_delete;
# endif
Eina_List *thread_children;
Eina_Inlist *message_queue;
unsigned int message_walking;
@ -176,7 +178,8 @@ struct _Efl_Loop_Data
char **environ_copy;
} env;
Eina_Bool do_quit;
Eina_Bool do_quit : 1;
Eina_Bool quit_on_last_thread_child_del : 1;
};
struct _Efl_Task_Data
@ -457,6 +460,8 @@ void _efl_loop_messages_call(Eo *obj, Efl_Loop_Data *pd, void *func, void *data)
void _efl_loop_message_send_info_set(Eo *obj, Eina_Inlist *node, Eo *loop, Efl_Loop_Data *loop_data);
void _efl_loop_message_unsend(Eo *obj);
void _efl_thread_child_remove(Eo *loop, Efl_Loop_Data *pd, Eo *child);
static inline Eina_Bool
_ecore_call_task_cb(Ecore_Task_Cb func,
void *data)

View File

@ -98,7 +98,7 @@ _close_fds(Efl_Exe_Data *pd)
}
static void
_exec(const char *cmd, Efl_Exe_Flags flags)
_exec(const char *cmd, Efl_Exe_Flags flags, Efl_Task_Flags task_flags)
{
char use_sh = 1, *buf = NULL, **args = NULL;
@ -149,7 +149,7 @@ _exec(const char *cmd, Efl_Exe_Flags flags)
}
}
# ifdef HAVE_PRCTL
if (flags & EFL_EXE_FLAGS_EXIT_WITH_PARENT)
if (task_flags & EFL_TASK_FLAGS_EXIT_WITH_PARENT)
prctl(PR_SET_PDEATHSIG, SIGTERM);
# endif
@ -603,7 +603,7 @@ _efl_exe_efl_task_run(Eo *obj, Efl_Exe_Data *pd)
}
// actually execute!
_exec(cmd, pd->flags);
_exec(cmd, pd->flags, td->flags);
// we couldn't exec... uh oh. HAAAAAAAALP!
if ((errno == EACCES) || (errno == EINVAL) || (errno == ELOOP) ||
(errno == ENOEXEC) || (errno == ENOMEM))
@ -641,7 +641,7 @@ _efl_exe_efl_object_constructor(Eo *obj, Efl_Exe_Data *pd)
pd->fd.exited_read = -1;
#endif
pd->fd.can_write = EINA_TRUE;
pd->flags = EFL_EXE_FLAGS_EXIT_WITH_PARENT;
pd->flags = 0;
pd->exit_signal = -1;
return obj;
}

View File

@ -18,7 +18,6 @@ enum @beta Efl.Exe_Flags {
[[Flags to customize task behavior.]] // TODO: This needs more detail.
none = 0, [[No special flags.]]
group_leader = 1, [[Process will be executed in its own session.]]
exit_with_parent = 2, [[Exit process when parent process exits.]]
hide_io = 4 [[All console IO will be hidden.]]
}

View File

@ -55,6 +55,26 @@ EOLIAN static Eina_Value *
_efl_loop_begin(Eo *obj, Efl_Loop_Data *pd)
{
_ecore_main_loop_begin(obj, pd);
if (pd->thread_children)
{
Eina_List *l, *ll;
Eo *child;
// request all child threads to die and defer the quit until
// the children have all died and returned.
// run main loop again to clean out children and their exits
pd->quit_on_last_thread_child_del = EINA_TRUE;
EINA_LIST_FOREACH_SAFE(pd->thread_children, l, ll, child)
{
Efl_Task_Flags task_flags = efl_task_flags_get(child);
if (task_flags & EFL_TASK_FLAGS_EXIT_WITH_PARENT)
efl_task_end(child);
else
_efl_thread_child_remove(obj, pd, child);
}
if (pd->thread_children) _ecore_main_loop_begin(obj, pd);
}
return &(pd->exit_code);
}
@ -304,7 +324,8 @@ EOLIAN static void
_efl_loop_efl_object_destructor(Eo *obj, Efl_Loop_Data *pd)
{
pd->future_message_handler = NULL;
while (pd->thread_children)
_efl_thread_child_remove(obj, pd, pd->thread_children->data);
efl_destructor(efl_super(obj, EFL_LOOP_CLASS));
}

View File

@ -50,6 +50,14 @@ _efl_task_efl_object_destructor(Eo *obj EINA_UNUSED, Efl_Task_Data *pd)
efl_destructor(efl_super(obj, MY_CLASS));
}
EOLIAN static Efl_Object *
_efl_task_efl_object_constructor(Eo *obj, Efl_Task_Data *pd)
{
obj = efl_constructor(efl_super(obj, EFL_TASK_CLASS));
pd->flags = EFL_TASK_FLAGS_EXIT_WITH_PARENT;
return obj;
}
EOLIAN static void
_efl_task_efl_object_parent_set(Eo *obj, Efl_Task_Data *pd EINA_UNUSED, Efl_Object *parent)
{

View File

@ -18,6 +18,7 @@ enum Efl.Task_Flags {
use_stdin = 1, [[Task will require console input.]]
use_stdout = 2, [[Task will require console output.]]
no_exit_code_error = 4, [[Task will not produce an exit code upon termination.]]
exit_with_parent = 8, [[Exit when parent exits.]]
}
abstract Efl.Task extends Efl.Loop_Consumer
@ -42,7 +43,9 @@ abstract Efl.Task extends Efl.Loop_Consumer
}
}
@property flags {
[[Flags to further customize task's behavior.]]
[[Flags to further customize task's behavior. The default value:
exit_with_parent
]]
set { }
get { }
values {
@ -62,6 +65,7 @@ abstract Efl.Task extends Efl.Loop_Consumer
events {
}
implements {
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Object.parent { set; }
}

View File

@ -372,6 +372,7 @@ _thread_exit_eval(Eo *obj, Efl_Thread_Data *pd)
eina_promise_reject(p, exit_code + 1000000);
else eina_promise_resolve(p, eina_value_int_init(exit_code));
}
efl_del(obj);
}
}
@ -567,6 +568,48 @@ _efl_thread_efl_object_constructor(Eo *obj, Efl_Thread_Data *pd)
return obj;
}
static void
_child_thread_del_cb(void *data, const Efl_Event *event)
{
Eo *loop = data;
Efl_Loop_Data *loop_data = efl_data_scope_get(loop, EFL_LOOP_CLASS);
if (!loop_data) return;
_efl_thread_child_remove(loop, loop_data, event->object);
if (!loop_data->quit_on_last_thread_child_del) return;
if (loop_data->thread_children) return;
// no more children waiting exits - quit the loop
_ecore_main_loop_quit(loop, loop_data);
}
EFL_CALLBACKS_ARRAY_DEFINE(thread_child_del,
{ EFL_EVENT_DEL, _child_thread_del_cb });
void
_efl_thread_child_remove(Eo *loop, Efl_Loop_Data *pd, Eo *child)
{
pd->thread_children = eina_list_remove(pd->thread_children, child);
efl_event_callback_array_del(child, thread_child_del(), loop);
}
EOLIAN static Efl_Object *
_efl_thread_efl_object_finalize(Eo *obj, Efl_Thread_Data *pd EINA_UNUSED)
{
Eo *loop = efl_provider_find(obj, EFL_LOOP_CLASS);
if (loop != obj)
{
Efl_Loop_Data *loop_data = efl_data_scope_get(loop, EFL_LOOP_CLASS);
if (loop_data)
{
loop_data->thread_children =
eina_list_prepend(loop_data->thread_children, obj);
efl_event_callback_array_add(obj, thread_child_del(), loop);
}
}
return obj;
}
EOLIAN static void
_efl_thread_efl_object_destructor(Eo *obj, Efl_Thread_Data *pd)
{
@ -834,7 +877,7 @@ EOLIAN static void
_efl_thread_efl_task_end(Eo *obj EINA_UNUSED, Efl_Thread_Data *pd)
{
if (pd->end_sent) return;
if (pd->thdat)
if ((pd->thdat) && (!pd->exit_called))
{
Control_Data cmd;

View File

@ -4,6 +4,7 @@ class @beta Efl.Thread extends Efl.Task implements Efl.ThreadIO, Efl.Io.Reader,
}
implements {
Efl.Object.constructor;
Efl.Object.finalize;
Efl.Object.destructor;
Efl.Object.parent { set; }
Efl.Task.run;