diff options
Diffstat (limited to 'src/lib/ecore/efl_thread.c')
-rw-r--r-- | src/lib/ecore/efl_thread.c | 1073 |
1 files changed, 1073 insertions, 0 deletions
diff --git a/src/lib/ecore/efl_thread.c b/src/lib/ecore/efl_thread.c new file mode 100644 index 0000000000..e39ed50d34 --- /dev/null +++ b/src/lib/ecore/efl_thread.c @@ -0,0 +1,1073 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#define EFL_IO_READER_PROTECTED 1 +#define EFL_IO_WRITER_PROTECTED 1 +#define EFL_IO_CLOSER_PROTECTED 1 + +#include <Ecore.h> + +#include "ecore_private.h" + +#include <unistd.h> +#include <fcntl.h> +#include <string.h> + +#define MY_CLASS EFL_THREAD_CLASS + +#define APPTHREAD_CLASS EFL_APPTHREAD_CLASS + +typedef struct +{ + const char *name; + struct { + int in, out; + Eo *in_handler, *out_handler; + } fd, ctrl; + struct { + unsigned int argc; + const char **argv; + } args; + Efl_Callback_Array_Item_Full *event_cb; + void *indata, *outdata; +} Thread_Data; + +#define CMD_EXIT 1 +#define CMD_EXITED 2 +#define CMD_CALL 3 +#define CMD_CALL_SYNC 4 + +typedef struct +{ + Eina_Semaphore sem; + void *data; +} Control_Reply; + +typedef struct +{ + union { + struct { + int command; + int data; + void *ptr[4]; + } d; + unsigned char b[64]; + }; +} Control_Data; + +typedef struct _Efl_Thread_Data Efl_Thread_Data; + +struct _Efl_Thread_Data +{ + struct { + int in, out; + Eo *in_handler, *out_handler; + Eina_Bool can_read : 1; + Eina_Bool eos_read : 1; + 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; + Eina_Thread thread; + Eina_Bool end_sent : 1; + Eina_Bool exit_read : 1; + Eina_Bool exit_called : 1; + Eina_Bool run : 1; +}; + +////////////////////////////////////////////////////////////////////////// + +static void +_cb_thread_out(void *data, const Efl_Event *event EINA_UNUSED) +{ + Eo *obj = data; + efl_io_reader_can_read_set(obj, EINA_TRUE); +} + +static void +_cb_thread_in(void *data, const Efl_Event *event EINA_UNUSED) +{ + Eo *obj = data; + efl_io_writer_can_write_set(obj, EINA_TRUE); +} + +static void +_cb_thread_ctrl_out(void *data, const Efl_Event *event EINA_UNUSED) +{ + Eo *obj = data; + Control_Data cmd; + ssize_t ret; + Efl_Appthread_Data *ad; + + ad = efl_data_scope_get(obj, APPTHREAD_CLASS); + memset(&cmd, 0, sizeof(cmd)); + ret = read(ad->ctrl.out, &cmd, sizeof(Control_Data)); + if (ret == sizeof(Control_Data)) + { + if (cmd.d.command == CMD_EXIT) + { + efl_event_callback_call(obj, EFL_LOOP_EVENT_QUIT, NULL); + efl_loop_quit(obj, eina_value_int_init(0)); + } + else if (cmd.d.command == CMD_CALL) + { + EFlThreadIOCall func = cmd.d.ptr[0]; + void *data = cmd.d.ptr[1]; + Eina_Free_Cb free_func = cmd.d.ptr[2]; + if (func) + { + Efl_Event event = { obj, NULL, NULL }; + + func(data, &event); + } + if (free_func) free_func(data); + } + else if (cmd.d.command == CMD_CALL_SYNC) + { + EFlThreadIOCallSync func = cmd.d.ptr[0]; + void *data = cmd.d.ptr[1]; + Eina_Free_Cb free_func = cmd.d.ptr[2]; + Control_Reply *rep = cmd.d.ptr[3]; + if (func) + { + Efl_Event event = { obj, NULL, NULL }; + + rep->data = func(data, &event); + } + if (free_func) free_func(data); + eina_semaphore_release(&(rep->sem), 1); + } + } +} + +static Eina_Value +_efl_loop_arguments_send(void *data, const Eina_Value v, + const Eina_Future *dead EINA_UNUSED) + +{ + Efl_Loop_Arguments arge; + Eo *obj = data; + Eina_Array *arga; + Eina_Stringshare *s; + unsigned int argc = efl_task_arg_count_get(obj); + unsigned int i; + + arga = eina_array_new(argc); + if (v.type == EINA_VALUE_TYPE_ERROR) goto on_error; + + for (i = 0; i < argc; i++) + { + const char *argv = efl_task_arg_value_get(obj, i); + if (argv) + eina_array_push(arga, eina_stringshare_add(argv)); + } + arge.argv = arga; + arge.initialization = EINA_TRUE; + efl_event_callback_call(obj, + EFL_LOOP_EVENT_ARGUMENTS, &arge); +on_error: + while ((s = eina_array_pop(arga))) eina_stringshare_del(s); + eina_array_free(arga); + return v; +} + +static void +_appthread_parent_read_listeners_modify(Efl_Appthread_Data *ad, int mod) +{ + ad->read_listeners += mod; + + if (ad->fd.out_handler) + { + if ((ad->read_listeners == 0) && (mod < 0)) + efl_loop_handler_active_set + (ad->fd.out_handler, EFL_LOOP_HANDLER_FLAGS_NONE); + else if ((ad->read_listeners == 1) && (mod > 0)) + efl_loop_handler_active_set + (ad->fd.out_handler, EFL_LOOP_HANDLER_FLAGS_READ); + } +} + +static void +_cb_appthread_event_callback_add(void *data, const Efl_Event *event) +{ + Efl_Appthread_Data *ad = data; + const Efl_Callback_Array_Item_Full *array = event->info; + int i; + + for (i = 0; array[i].desc != NULL; i++) + { + if (array[i].desc == EFL_IO_READER_EVENT_CAN_READ_CHANGED) + _appthread_parent_read_listeners_modify(ad, 1); + } +} + +static void +_cb_appthread_event_callback_del(void *data, const Efl_Event *event) +{ + Efl_Appthread_Data *ad = data; + const Efl_Callback_Array_Item_Full *array = event->info; + int i; + + for (i = 0; array[i].desc != NULL; i++) + { + if (array[i].desc == EFL_IO_READER_EVENT_CAN_READ_CHANGED) + _appthread_parent_read_listeners_modify(ad, -1); + } +} + +EFL_CALLBACKS_ARRAY_DEFINE(_appthread_event_callback_watch, + { EFL_EVENT_CALLBACK_ADD, _cb_appthread_event_callback_add }, + { EFL_EVENT_CALLBACK_DEL, _cb_appthread_event_callback_del }); + +static void * +_efl_thread_main(void *data, Eina_Thread t) +{ + Efl_Appthread_Data *ad; + Thread_Data *thdat = data; + Eo *obj; + Eina_Value *ret; + Control_Data cmd; + unsigned int i; + int real; + Efl_Callback_Array_Item_Full *it; + Eina_Future *job; + + if (thdat->name) eina_thread_name_set(t, thdat->name); + else eina_thread_name_set(t, "Eflthread"); + + obj = efl_add_ref(APPTHREAD_CLASS, NULL); + ad = efl_data_scope_get(obj, APPTHREAD_CLASS); + efl_threadio_indata_set(obj, thdat->indata); + efl_threadio_outdata_set(obj, thdat->outdata); + efl_event_callback_array_add(obj, _appthread_event_callback_watch(), ad); + + // add handlers for "stdio" + thdat->fd.in_handler = + efl_add(EFL_LOOP_HANDLER_CLASS, obj, + efl_loop_handler_fd_set(efl_added, thdat->fd.in), + efl_event_callback_add + (efl_added, EFL_LOOP_HANDLER_EVENT_WRITE, _cb_thread_in, obj)); + thdat->fd.out_handler = + efl_add(EFL_LOOP_HANDLER_CLASS, obj, + efl_loop_handler_fd_set(efl_added, thdat->fd.out), + efl_event_callback_add + (efl_added, EFL_LOOP_HANDLER_EVENT_READ, _cb_thread_out, obj)); + // add handlers for control pipes + thdat->ctrl.in_handler = + efl_add(EFL_LOOP_HANDLER_CLASS, obj, + efl_loop_handler_fd_set(efl_added, thdat->ctrl.in)); + thdat->ctrl.out_handler = + efl_add(EFL_LOOP_HANDLER_CLASS, obj, + efl_loop_handler_fd_set(efl_added, thdat->ctrl.out), + efl_event_callback_add + (efl_added, EFL_LOOP_HANDLER_EVENT_READ, _cb_thread_ctrl_out, obj), + efl_loop_handler_active_set + (efl_added, EFL_LOOP_HANDLER_FLAGS_READ)); + ad->fd.in = thdat->fd.in; + ad->fd.out = thdat->fd.out; + ad->ctrl.in = thdat->ctrl.in; + ad->ctrl.out = thdat->ctrl.out; + ad->fd.in_handler = thdat->fd.in_handler; + ad->fd.out_handler = thdat->fd.out_handler; + ad->ctrl.in_handler = thdat->ctrl.in_handler; + ad->ctrl.out_handler = thdat->ctrl.out_handler; + ad->thdat = thdat; + + if (thdat->event_cb) + { + for (it = thdat->event_cb; it->func; it++) + efl_event_callback_priority_add(obj, it->desc, it->priority, + it->func, it->user_data); + } + for (i = 0; i < thdat->args.argc; i++) + efl_task_arg_append(obj, thdat->args.argv[i]); + job = eina_future_then(efl_loop_job(obj), _efl_loop_arguments_send, obj); + efl_future_Eina_FutureXXX_then(obj, job); + + for (i = 0; i < thdat->args.argc; i++) + eina_stringshare_del(thdat->args.argv[i]); + free(thdat->args.argv); + free(thdat->event_cb); + thdat->args.argv = NULL; + thdat->event_cb = NULL; + + ret = efl_loop_begin(obj); + real = efl_loop_exit_code_process(ret); + thdat->outdata = efl_threadio_outdata_get(obj); + + memset(&cmd, 0, sizeof(cmd)); + cmd.d.command = CMD_EXITED; + cmd.d.data = real; + write(thdat->ctrl.in, &cmd, sizeof(Control_Data)); + + efl_unref(obj); + + thdat->fd.in_handler = NULL; + thdat->fd.out_handler = NULL; + thdat->ctrl.in_handler = NULL; + thdat->ctrl.out_handler = NULL; + thdat->fd.in = -1; + thdat->fd.out = -1; + thdat->ctrl.in = -1; + thdat->ctrl.out = -1; + + return NULL; +} + +////////////////////////////////////////////////////////////////////////// + +static void +_thread_exit_eval(Eo *obj, Efl_Thread_Data *pd) +{ + if ((pd->fd.out == -1) && /*(pd->fd.in == -1) &&*/ + (pd->exit_read) && (!pd->exit_called)) + { + 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); + pd->promise = NULL; + 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)); + } + } +} + +static void +_cb_thread_parent_out(void *data, const Efl_Event *event EINA_UNUSED) +{ + Eo *obj = data; + efl_io_reader_can_read_set(obj, EINA_TRUE); +} + +static void +_cb_thread_parent_in(void *data, const Efl_Event *event EINA_UNUSED) +{ + Eo *obj = data; + efl_io_writer_can_write_set(obj, EINA_TRUE); +} + +static void +_cb_thread_parent_ctrl_out(void *data, const Efl_Event *event EINA_UNUSED) +{ + Eo *obj = data; + Control_Data cmd; + ssize_t ret; + Efl_Thread_Data *pd = efl_data_scope_get(obj, MY_CLASS); + + if (!pd) return; + + memset(&cmd, 0, sizeof(cmd)); + ret = read(pd->ctrl.out, &cmd, sizeof(Control_Data)); + if (ret == sizeof(Control_Data)) + { + if (cmd.d.command == CMD_EXITED) + { + if (!pd->exit_read) + { + Efl_Task_Data *td = efl_data_scope_get(obj, EFL_TASK_CLASS); + + if (td) td->exit_code = cmd.d.data; + pd->exit_read = EINA_TRUE; + _thread_exit_eval(obj, pd); + } + } + else if (cmd.d.command == CMD_CALL) + { + EFlThreadIOCall func = cmd.d.ptr[0]; + void *data = cmd.d.ptr[1]; + Eina_Free_Cb free_func = cmd.d.ptr[2]; + if (func) + { + Efl_Event event = { obj, NULL, NULL }; + + func(data, &event); + } + if (free_func) free_func(data); + } + else if (cmd.d.command == CMD_CALL_SYNC) + { + EFlThreadIOCallSync func = cmd.d.ptr[0]; + void *data = cmd.d.ptr[1]; + Eina_Free_Cb free_func = cmd.d.ptr[2]; + Control_Reply *rep = cmd.d.ptr[3]; + if (func) + { + Efl_Event event = { obj, NULL, NULL }; + + rep->data = func(data, &event); + } + if (free_func) free_func(data); + eina_semaphore_release(&(rep->sem), 1); + } + } +} + +////////////////////////////////////////////////////////////////////////// + +static void +_run_cancel_cb(void *data, const Eina_Promise *dead_promise EINA_UNUSED) +{ + Eo *obj = data; + Efl_Thread_Data *pd = efl_data_scope_get(obj, MY_CLASS); + pd->promise = NULL; + efl_task_end(obj); +} + +static void +_thread_parent_read_listeners_modify(Efl_Thread_Data *pd, int mod) +{ + pd->read_listeners += mod; + + if (pd->fd.out_handler) + { + if ((pd->read_listeners == 0) && (mod < 0)) + efl_loop_handler_active_set + (pd->fd.out_handler, EFL_LOOP_HANDLER_FLAGS_NONE); + else if ((pd->read_listeners == 1) && (mod > 0)) + efl_loop_handler_active_set + (pd->fd.out_handler, EFL_LOOP_HANDLER_FLAGS_READ); + } +} + +static void +_cb_event_callback_add(void *data, const Efl_Event *event) +{ + Efl_Thread_Data *pd = data; + const Efl_Callback_Array_Item_Full *array = event->info; + int i; + + for (i = 0; array[i].desc != NULL; i++) + { + if (array[i].desc == EFL_LOOP_EVENT_ARGUMENTS) + { + Efl_Callback_Array_Item_Full *event_cb, *it; + int num; + + num = 0; + if (pd->event_cb) + { + for (it = pd->event_cb; it->func; it++) num++; + } + num++; + event_cb = realloc(pd->event_cb, (num + 1) * sizeof(Efl_Callback_Array_Item_Full)); + if (event_cb) + { + pd->event_cb = event_cb; + event_cb[num - 1] = array[i]; + event_cb[num].desc = NULL; + event_cb[num].priority = 0; + event_cb[num].func = NULL; + event_cb[num].user_data = NULL; + } + } + else if (array[i].desc == EFL_IO_READER_EVENT_CAN_READ_CHANGED) + _thread_parent_read_listeners_modify(pd, 1); + } +} + +static void +_cb_event_callback_del(void *data, const Efl_Event *event) +{ + Efl_Thread_Data *pd = data; + const Efl_Callback_Array_Item_Full *array = event->info; + int i; + + for (i = 0; array[i].desc != NULL; i++) + { + if (array[i].desc == EFL_LOOP_EVENT_ARGUMENTS) + { + Efl_Callback_Array_Item_Full *it; + + if (pd->event_cb) + { + Eina_Bool shuffle_down = EINA_FALSE; + + for (it = pd->event_cb; it->func; it++) + { + if ((it->desc == array[i].desc) && + (it->priority == array[i].priority) && + (it->func == array[i].func) && + (it->user_data == array[i].user_data)) + shuffle_down = EINA_TRUE; + if (shuffle_down) it[0] = it[1]; + } + } + } + else if (array[i].desc == EFL_IO_READER_EVENT_CAN_READ_CHANGED) + _thread_parent_read_listeners_modify(pd, -1); + } +} + +EFL_CALLBACKS_ARRAY_DEFINE(_event_callback_watch, + { EFL_EVENT_CALLBACK_ADD, _cb_event_callback_add }, + { EFL_EVENT_CALLBACK_DEL, _cb_event_callback_del }); + +////////////////////////////////////////////////////////////////////////// + +EOLIAN static Efl_Object * +_efl_thread_efl_object_constructor(Eo *obj, Efl_Thread_Data *pd) +{ + obj = efl_constructor(efl_super(obj, MY_CLASS)); + efl_event_callback_array_add(obj, _event_callback_watch(), pd); + pd->fd.in = -1; + pd->fd.out = -1; + pd->fd.can_write = EINA_TRUE; + pd->ctrl.in = -1; + pd->ctrl.out = -1; + return obj; +} + +EOLIAN static void +_efl_thread_efl_object_destructor(Eo *obj, Efl_Thread_Data *pd) +{ + if (pd->promise) + ERR("Thread being destroyed while real worker has not exited yet."); + if (pd->thdat) + { + eina_thread_join(pd->thread); + efl_del(pd->fd.in_handler); + efl_del(pd->fd.out_handler); + efl_del(pd->ctrl.in_handler); + efl_del(pd->ctrl.out_handler); + close(pd->fd.in); + close(pd->fd.out); + close(pd->ctrl.in); + close(pd->ctrl.out); + pd->fd.in_handler = NULL; + pd->fd.out_handler = NULL; + pd->ctrl.in_handler = NULL; + pd->ctrl.out_handler = NULL; + pd->fd.in = -1; + pd->fd.out = -1; + pd->ctrl.in = -1; + pd->ctrl.out = -1; + free(pd->thdat); + } + free(pd->event_cb); + pd->event_cb = NULL; + efl_destructor(efl_super(obj, MY_CLASS)); +} + +EOLIAN static void +_efl_thread_efl_object_parent_set(Eo *obj, Efl_Thread_Data *pd, Efl_Object *parent) +{ + efl_parent_set(efl_super(obj, MY_CLASS), parent); + pd->loop = efl_provider_find(parent, EFL_LOOP_CLASS); +} + +EOLIAN static Eina_Future * +_efl_thread_efl_task_run(Eo *obj, Efl_Thread_Data *pd) +{ + Eina_Thread_Priority pri; + Thread_Data *thdat; + const char *name; + int pipe_to_thread[2]; + int pipe_from_thread[2]; + unsigned int argc, i, num; + 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; + thdat = calloc(1, sizeof(Thread_Data)); + if (!thdat) return NULL; + thdat->fd.in = -1; + thdat->fd.out = -1; + thdat->ctrl.in = -1; + thdat->ctrl.out = -1; + + thdat->indata = efl_threadio_indata_get(obj); + thdat->outdata = efl_threadio_outdata_get(obj); + + // input/output pipes + if (td->flags & EFL_TASK_FLAGS_USE_STDIN) + { + if (pipe(pipe_to_thread) != 0) + { + ERR("Can't create to_thread pipe"); + free(thdat); + return NULL; + } + } + if (td->flags & EFL_TASK_FLAGS_USE_STDOUT) + { + if (pipe(pipe_from_thread) != 0) + { + ERR("Can't create from_thread pipe"); + close(pipe_to_thread[0]); + close(pipe_to_thread[1]); + free(thdat); + return NULL; + } + } + if (td->flags & EFL_TASK_FLAGS_USE_STDIN) + { + thdat->fd.in = pipe_from_thread[1]; // write - input to parent + pd->fd.out = pipe_from_thread[0]; // read - output from child + eina_file_close_on_exec(thdat->fd.in, EINA_TRUE); + eina_file_close_on_exec(pd->fd.out, EINA_TRUE); + fcntl(thdat->fd.in, F_SETFL, O_NONBLOCK); + fcntl(pd->fd.out, F_SETFL, O_NONBLOCK); + pd->fd.out_handler = + efl_add(EFL_LOOP_HANDLER_CLASS, obj, + efl_loop_handler_fd_set(efl_added, pd->fd.out), + efl_event_callback_add + (efl_added, EFL_LOOP_HANDLER_EVENT_READ, _cb_thread_parent_out, obj)); + if (pd->read_listeners > 0) + efl_loop_handler_active_set(pd->fd.out_handler, EFL_LOOP_HANDLER_FLAGS_READ); + } + if (td->flags & EFL_TASK_FLAGS_USE_STDOUT) + { + pd->fd.in = pipe_to_thread [1]; // write - input to child + thdat->fd.out = pipe_to_thread [0]; // read - output from parent + eina_file_close_on_exec(pd->fd.in, EINA_TRUE); + eina_file_close_on_exec(thdat->fd.out, EINA_TRUE); + fcntl(thdat->fd.out, F_SETFL, O_NONBLOCK); + fcntl(pd->fd.in, F_SETFL, O_NONBLOCK); + pd->fd.in_handler = + efl_add(EFL_LOOP_HANDLER_CLASS, obj, + efl_loop_handler_fd_set(efl_added, pd->fd.in), + efl_event_callback_add + (efl_added, EFL_LOOP_HANDLER_EVENT_WRITE, _cb_thread_parent_in, obj)); + } + + // control pipes + if (pipe(pipe_to_thread) != 0) + { + ERR("Can't create to_thread control pipe"); + efl_del(pd->fd.in_handler); + efl_del(pd->fd.out_handler); + close(thdat->fd.in); + close(thdat->fd.out); + close(pd->fd.in); + close(pd->fd.out); + pd->fd.in_handler = NULL; + pd->fd.out_handler = NULL; + pd->fd.in = -1; + pd->fd.out = -1; + free(thdat); + return NULL; + } + if (pipe(pipe_from_thread) != 0) + { + ERR("Can't create from_thread control pipe"); + efl_del(pd->fd.in_handler); + efl_del(pd->fd.out_handler); + close(pipe_to_thread[0]); + close(pipe_to_thread[1]); + close(thdat->fd.in); + close(thdat->fd.out); + close(pd->fd.in); + close(pd->fd.out); + pd->fd.in_handler = NULL; + pd->fd.out_handler = NULL; + pd->fd.in = -1; + pd->fd.out = -1; + free(thdat); + return NULL; + } + thdat->ctrl.in = pipe_from_thread[1]; // write - input to parent + thdat->ctrl.out = pipe_to_thread [0]; // read - output from parent + pd->ctrl.in = pipe_to_thread [1]; // write - input to child + pd->ctrl.out = pipe_from_thread[0]; // read - output from child + fcntl(thdat->ctrl.in, F_SETFL, O_NONBLOCK); + fcntl(thdat->ctrl.out, F_SETFL, O_NONBLOCK); + fcntl(pd->ctrl.in, F_SETFL, O_NONBLOCK); + fcntl(pd->ctrl.out, F_SETFL, O_NONBLOCK); + eina_file_close_on_exec(pd->ctrl.in, EINA_TRUE); + eina_file_close_on_exec(pd->ctrl.out, EINA_TRUE); + eina_file_close_on_exec(thdat->ctrl.in, EINA_TRUE); + eina_file_close_on_exec(thdat->ctrl.out, EINA_TRUE); + pd->ctrl.in_handler = + efl_add(EFL_LOOP_HANDLER_CLASS, obj, + efl_loop_handler_fd_set(efl_added, pd->ctrl.in)); + pd->ctrl.out_handler = + efl_add(EFL_LOOP_HANDLER_CLASS, obj, + efl_loop_handler_fd_set(efl_added, pd->ctrl.out), + efl_event_callback_add + (efl_added, EFL_LOOP_HANDLER_EVENT_READ, _cb_thread_parent_ctrl_out, obj), + efl_loop_handler_active_set + (efl_added, EFL_LOOP_HANDLER_FLAGS_READ)); + + switch (efl_task_priority_get(obj)) + { + case EFL_TASK_PRIORITY_BACKGROUND: + pri = EINA_THREAD_IDLE; + break; + case EFL_TASK_PRIORITY_HIGH: + case EFL_TASK_PRIORITY_ULTRA: + pri = EINA_THREAD_URGENT; + break; + case EFL_TASK_PRIORITY_LOW: + pri = EINA_THREAD_BACKGROUND; + break; + case EFL_TASK_PRIORITY_NORMAL: + default: + pri = EINA_THREAD_NORMAL; + break; + } + name = efl_name_get(obj); + if (name) thdat->name = eina_stringshare_add(name); + + argc = efl_task_arg_count_get(obj); + if (argc > 0) + { + thdat->args.argc = argc; + thdat->args.argv = malloc(argc * sizeof(char *)); + if (thdat->args.argv) + { + for (i = 0; i < argc; i++) + { + const char *argv = efl_task_arg_value_get(obj, i); + if (argv) + thdat->args.argv[i] = eina_stringshare_add(argv); + else + thdat->args.argv[i] = NULL; + } + } + // XXX: if malloc fails? + } + if (pd->event_cb) + { + num = 0; + for (it = pd->event_cb; it->func; it++) num++; + thdat->event_cb = malloc((num + 1) * sizeof(Efl_Callback_Array_Item_Full)); + if (thdat->event_cb) + memcpy(thdat->event_cb, pd->event_cb, + (num + 1) * sizeof(Efl_Callback_Array_Item_Full)); + // XXX: if malloc fails? + } + + // env data - ignore as other end will share same env + + if (!eina_thread_create(&(pd->thread), pri, -1, _efl_thread_main, thdat)) + { + for (i = 0; i < thdat->args.argc; i++) + eina_stringshare_del(thdat->args.argv[i]); + free(thdat->args.argv); + efl_del(pd->fd.in_handler); + efl_del(pd->fd.out_handler); + efl_del(pd->ctrl.in_handler); + efl_del(pd->ctrl.out_handler); + close(pd->fd.in); + close(pd->fd.out); + close(pd->ctrl.in); + close(pd->ctrl.out); + close(thdat->fd.in); + close(thdat->fd.out); + close(thdat->ctrl.in); + close(thdat->ctrl.out); + free(thdat); + pd->fd.in_handler = NULL; + pd->fd.out_handler = NULL; + pd->ctrl.in_handler = NULL; + pd->ctrl.out_handler = NULL; + pd->fd.in = -1; + pd->fd.out = -1; + pd->ctrl.in = -1; + pd->ctrl.out = -1; + return NULL; + } + pd->thdat = thdat; + pd->run = EINA_TRUE; + pd->promise = efl_loop_promise_new(obj, _run_cancel_cb, obj); + Eina_Future *f = eina_future_new(pd->promise); + return efl_future_Eina_FutureXXX_then(obj, f); +} + +EOLIAN static void +_efl_thread_efl_task_end(Eo *obj EINA_UNUSED, Efl_Thread_Data *pd) +{ + if (pd->end_sent) return; + if (pd->thdat) + { + Control_Data cmd; + + pd->end_sent = EINA_TRUE; + memset(&cmd, 0, sizeof(cmd)); + cmd.d.command = CMD_EXIT; + write(pd->ctrl.in, &cmd, sizeof(Control_Data)); + } +} + +EOLIAN static Eina_Error +_efl_thread_efl_io_closer_close(Eo *obj, Efl_Thread_Data *pd) +{ + if (!pd->thdat) return 0; + EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(obj), EBADF); + efl_io_writer_can_write_set(obj, EINA_FALSE); + efl_io_reader_can_read_set(obj, EINA_FALSE); + efl_io_reader_eos_set(obj, EINA_TRUE); + if (pd->fd.in >= 0) close(pd->fd.in); + if (pd->fd.out >= 0) close(pd->fd.out); + if (pd->fd.in_handler) efl_del(pd->fd.in_handler); + if (pd->fd.out_handler) efl_del(pd->fd.out_handler); + pd->fd.in = -1; + pd->fd.out = -1; + pd->fd.in_handler = NULL; + pd->fd.out_handler = NULL; + return 0; +} + +EOLIAN static Eina_Bool +_efl_thread_efl_io_closer_closed_get(const Eo *obj EINA_UNUSED, Efl_Thread_Data *pd) +{ + if ((pd->fd.in == -1) && (pd->fd.out == -1)) return EINA_TRUE; + return EINA_FALSE; +} + +EOLIAN static Eina_Error +_efl_thread_efl_io_reader_read(Eo *obj, Efl_Thread_Data *pd, Eina_Rw_Slice *rw_slice) +{ + ssize_t r; + + errno = 0; + if (pd->fd.out == -1) goto err; + + do + { + errno = 0; + r = read(pd->fd.out, rw_slice->mem, rw_slice->len); + if (r == -1) + { + if (errno == EINTR) continue; + goto err; + } + } + while (r == -1); + + rw_slice->len = r; + if (r == 0) + { + efl_io_reader_can_read_set(obj, EINA_FALSE); + efl_io_reader_eos_set(obj, EINA_TRUE); + close(pd->fd.out); + pd->fd.out = -1; + efl_del(pd->fd.out_handler); + pd->fd.out_handler = NULL; + _thread_exit_eval(obj, pd); + return EPIPE; + } + return 0; +err: + if ((pd->fd.out != -1) && (errno != EAGAIN)) + { + close(pd->fd.out); + pd->fd.out = -1; + efl_del(pd->fd.out_handler); + pd->fd.out_handler = NULL; + } + rw_slice->len = 0; + rw_slice->mem = NULL; + efl_io_reader_can_read_set(obj, EINA_FALSE); + _thread_exit_eval(obj, pd); + return EINVAL; +} + +EOLIAN static void +_efl_thread_efl_io_reader_can_read_set(Eo *obj, Efl_Thread_Data *pd, Eina_Bool can_read) +{ + Eina_Bool old = efl_io_reader_can_read_get(obj); + if (old == can_read) return; + pd->fd.can_read = can_read; + if (!pd->fd.out_handler) return; + if (can_read) + efl_loop_handler_active_set(pd->fd.out_handler, 0); + else + efl_loop_handler_active_set(pd->fd.out_handler, + EFL_LOOP_HANDLER_FLAGS_READ); + efl_event_callback_call(obj, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL); +} + +EOLIAN static Eina_Bool +_efl_thread_efl_io_reader_can_read_get(const Eo *obj EINA_UNUSED, Efl_Thread_Data *pd) +{ + return pd->fd.can_read; +} + +EOLIAN static void +_efl_thread_efl_io_reader_eos_set(Eo *obj, Efl_Thread_Data *pd, Eina_Bool is_eos) +{ + Eina_Bool old = efl_io_reader_eos_get(obj); + if (old == is_eos) return; + + pd->fd.eos_read = is_eos; + if (!is_eos) return; + if (pd->fd.out_handler) + efl_loop_handler_active_set(pd->fd.out_handler, 0); + efl_event_callback_call(obj, EFL_IO_READER_EVENT_EOS, NULL); +} + +EOLIAN static Eina_Bool +_efl_thread_efl_io_reader_eos_get(const Eo *obj EINA_UNUSED, Efl_Thread_Data *pd) +{ + return pd->fd.eos_read; +} + +EOLIAN static Eina_Error +_efl_thread_efl_io_writer_write(Eo *obj, Efl_Thread_Data *pd, Eina_Slice *slice, Eina_Slice *remaining) +{ + ssize_t r; + + errno = 0; + if (pd->fd.in == -1) goto err; + + do + { + errno = 0; + r = write(pd->fd.in, slice->mem, slice->len); + if (r == -1) + { + if (errno == EINTR) continue; + goto err; + } + } + while (r == -1); + + if (remaining) + { + remaining->len = slice->len - r; + remaining->bytes = slice->bytes + r; + } + slice->len = r; + + if ((slice) && (slice->len > 0)) + efl_io_writer_can_write_set(obj, EINA_FALSE); + if (r == 0) + { + close(pd->fd.in); + pd->fd.in = -1; + efl_del(pd->fd.in_handler); + pd->fd.in_handler = NULL; + _thread_exit_eval(obj, pd); + return EPIPE; + } + return 0; +err: + if ((pd->fd.in != -1) && (errno != EAGAIN)) + { + close(pd->fd.in); + pd->fd.in = -1; + efl_del(pd->fd.in_handler); + pd->fd.in_handler = NULL; + } + if (remaining) *remaining = *slice; + slice->len = 0; + slice->mem = NULL; + efl_io_writer_can_write_set(obj, EINA_FALSE); + _thread_exit_eval(obj, pd); + return EINVAL; +} + +EOLIAN static void +_efl_thread_efl_io_writer_can_write_set(Eo *obj, Efl_Thread_Data *pd, Eina_Bool can_write) +{ + Eina_Bool old = efl_io_writer_can_write_get(obj); + if (old == can_write) return; + pd->fd.can_write = can_write; + if (can_write) + efl_loop_handler_active_set(pd->fd.in_handler, 0); + else + efl_loop_handler_active_set(pd->fd.in_handler, + EFL_LOOP_HANDLER_FLAGS_WRITE); + efl_event_callback_call(obj, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL); +} + +EOLIAN static Eina_Bool +_efl_thread_efl_io_writer_can_write_get(const Eo *obj EINA_UNUSED, Efl_Thread_Data *pd) +{ + return pd->fd.can_write; +} + +void +_appthread_threadio_call(Eo *obj EINA_UNUSED, Efl_Appthread_Data *pd, + void *func_data, EFlThreadIOCall func, Eina_Free_Cb func_free_cb) +{ + Thread_Data *thdat = pd->thdat; + Control_Data cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.d.command = CMD_CALL; + cmd.d.ptr[0] = func; + cmd.d.ptr[1] = func_data; + cmd.d.ptr[2] = func_free_cb; + write(thdat->ctrl.in, &cmd, sizeof(Control_Data)); +} + +EOLIAN static void +_efl_thread_efl_threadio_call(Eo *obj EINA_UNUSED, Efl_Thread_Data *pd, + void *func_data, EFlThreadIOCall func, Eina_Free_Cb func_free_cb) +{ + Control_Data cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.d.command = CMD_CALL; + cmd.d.ptr[0] = func; + cmd.d.ptr[1] = func_data; + cmd.d.ptr[2] = func_free_cb; + write(pd->ctrl.in, &cmd, sizeof(Control_Data)); +} + +void * +_appthread_threadio_call_sync(Eo *obj EINA_UNUSED, Efl_Appthread_Data *pd, + void *func_data, EFlThreadIOCallSync func, Eina_Free_Cb func_free_cb) +{ + Thread_Data *thdat = pd->thdat; + Control_Data cmd; + Control_Reply *rep; + void *data; + + memset(&cmd, 0, sizeof(cmd)); + cmd.d.command = CMD_CALL_SYNC; + cmd.d.ptr[0] = func; + cmd.d.ptr[1] = func_data; + cmd.d.ptr[2] = func_free_cb; + rep = malloc(sizeof(Control_Reply)); + if (!rep) return NULL; + cmd.d.ptr[3] = rep; + rep->data = NULL; + eina_semaphore_new(&(rep->sem), 0); + write(thdat->ctrl.in, &cmd, sizeof(Control_Data)); + eina_semaphore_lock(&(rep->sem)); + data = rep->data; + free(rep); + return data; +} + +EOLIAN static void * +_efl_thread_efl_threadio_call_sync(Eo *obj EINA_UNUSED, Efl_Thread_Data *pd, + void *func_data, EFlThreadIOCallSync func, Eina_Free_Cb func_free_cb) +{ + Control_Data cmd; + Control_Reply *rep; + void *data; + + memset(&cmd, 0, sizeof(cmd)); + cmd.d.command = CMD_CALL_SYNC; + cmd.d.ptr[0] = func; + cmd.d.ptr[1] = func_data; + cmd.d.ptr[2] = func_free_cb; + rep = malloc(sizeof(Control_Reply)); + if (!rep) return NULL; + cmd.d.ptr[3] = rep; + rep->data = NULL; + eina_semaphore_new(&(rep->sem), 0); + write(pd->ctrl.in, &cmd, sizeof(Control_Data)); + eina_semaphore_lock(&(rep->sem)); + data = rep->data; + free(rep); + return data; +} + +////////////////////////////////////////////////////////////////////////// + +#include "efl_thread.eo.c" |