ecore - start work on efl task/exe/thread

this is  astart of the work for having a common task class/interface
between loops, threads ane exe's so the i/o is all symmetric and works
the same way between all of them as well as similarly for launching
and knowing when the exit etc. etc.

this is not final and not perfect, but it's a start. comments of
course welcome
This commit is contained in:
Carsten Haitzler 2018-02-21 15:44:11 +09:00
parent d7a22256b6
commit 47ff2d8126
12 changed files with 1778 additions and 5 deletions

View File

@ -14,6 +14,8 @@ ecore_eolian_files_public = \
lib/ecore/efl_loop_handler.eo \
lib/ecore/efl_loop_message.eo \
lib/ecore/efl_loop_message_handler.eo \
lib/ecore/efl_exe.eo \
lib/ecore/efl_thread.eo \
lib/ecore/efl_io_closer_fd.eo \
lib/ecore/efl_io_positioner_fd.eo \
lib/ecore/efl_io_reader_fd.eo \
@ -33,8 +35,8 @@ ecore_eolian_files_public = \
lib/ecore/efl_interpolator_divisor.eo \
lib/ecore/efl_interpolator_bounce.eo \
lib/ecore/efl_interpolator_spring.eo \
lib/ecore/efl_interpolator_cubic_bezier.eo
lib/ecore/efl_interpolator_cubic_bezier.eo \
lib/ecore/efl_task.eo
ecore_eolian_files = \
$(ecore_eolian_files_public) \
@ -130,6 +132,8 @@ lib/ecore/efl_interpolator_divisor.c \
lib/ecore/efl_interpolator_bounce.c \
lib/ecore/efl_interpolator_spring.c \
lib/ecore/efl_interpolator_cubic_bezier.c \
lib/ecore/efl_task.c \
lib/ecore/efl_exe.c \
lib/ecore/ecore_main_timechanges.c \
lib/ecore/ecore_pipe.c \
lib/ecore/ecore_poller.c \

View File

@ -44,6 +44,9 @@
#include "efl_loop_message.eo.h"
#include "efl_loop_message_handler.eo.h"
#include "efl_task.eo.h"
#include "efl_exe.eo.h"
#include "efl_loop.eo.h"
/**

View File

@ -106,6 +106,8 @@ static Eina_Condition _thread_cond;
static Eina_Lock _thread_feedback_mutex;
static Eina_Condition _thread_feedback_cond;
Eina_Lock _environ_lock;
static Eina_Lock _thread_id_lock;
static int _thread_id = -1;
static int _thread_id_max = 0;
@ -267,6 +269,8 @@ ecore_init(void)
goto shutdown_log_dom;
}
eina_lock_new(&_environ_lock);
efl_object_init();
if (getenv("ECORE_FPS_DEBUG")) _ecore_fps_debug = 1;
@ -440,7 +444,6 @@ ecore_shutdown(void)
eina_lock_free(&_thread_feedback_mutex);
eina_lock_free(&_thread_id_lock);
#ifndef HAVE_EXOTIC
_ecore_exe_shutdown();
#endif
@ -482,6 +485,8 @@ ecore_shutdown(void)
efl_object_shutdown();
eina_lock_free(&_environ_lock);
eina_evlog("<RUN", NULL, 0.0, NULL);
eina_shutdown();
#ifdef _WIN32

View File

@ -87,6 +87,8 @@ typedef struct _Efl_Loop_Timer_Data Efl_Loop_Timer_Data;
typedef struct _Efl_Loop_Future_Scheduler Efl_Loop_Future_Scheduler;
typedef struct _Efl_Loop_Data Efl_Loop_Data;
typedef struct _Efl_Task_Data Efl_Task_Data;
typedef struct _Message_Handler Message_Handler;
typedef struct _Message Message;
@ -172,9 +174,26 @@ struct _Efl_Loop_Data
int low;
} pollers;
struct {
char **environ_ptr;
char **environ_copy;
} env;
Eina_Bool do_quit;
};
struct _Efl_Task_Data
{
Eina_Stringshare *command;
Eina_Array *args;
Eina_Hash *env;
Efl_Task_Priority priority;
int exit_code;
Eina_Bool command_dirty : 1;
Eina_Bool exited : 1;
};
#define EVAS_FRAME_QUEUING 1 /* for test */
#define READBUFSIZ 65536
@ -321,11 +340,27 @@ static inline int _ecore_signal_count_get(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd
static inline void _ecore_signal_call(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED) { }
#else
#define ECORE_SIGNALS 1
typedef struct _Ecore_Signal_Pid_Info Ecore_Signal_Pid_Info;
struct _Ecore_Signal_Pid_Info
{
pid_t pid;
int exit_code;
int exit_signal;
siginfo_t info;
};
void _ecore_signal_shutdown(void);
void _ecore_signal_init(void);
void _ecore_signal_received_process(Eo *obj, Efl_Loop_Data *pd);
int _ecore_signal_count_get(Eo *obj, Efl_Loop_Data *pd);
void _ecore_signal_call(Eo *obj, Efl_Loop_Data *pd);
void _ecore_signal_pid_lock(void);
void _ecore_signal_pid_unlock(void);
void _ecore_signal_pid_register(pid_t pid, int fd);
void _ecore_signal_pid_unregister(pid_t pid, int fd);
#endif
void _ecore_exe_init(void);

View File

@ -20,6 +20,14 @@
/* make mono happy - this is evil though... */
#undef SIGPWR
typedef struct _Pid_Info Pid_Info;
struct _Pid_Info
{
pid_t pid;
int fd;
};
static void _ecore_signal_exe_exit_delay(void *data, const Efl_Event *event);
static void _ecore_signal_waitpid(Eina_Bool once, siginfo_t info);
static void _ecore_signal_generic_free(void *data, void *event);
@ -28,6 +36,8 @@ typedef void (*Signal_Handler)(int sig, siginfo_t *si, void *foo);
static int sig_pipe[2] = { -1, -1 }; // [0] == read, [1] == write
static Eo *sig_pipe_handler = NULL;
static Eina_Spinlock sig_pid_lock;
static Eina_List *sig_pid_info_list = NULL;
typedef struct _Signal_Data
{
@ -204,6 +214,7 @@ _signalhandler_setup(void)
static void
_ecore_signal_pipe_init(void)
{
eina_spinlock_new(&sig_pid_lock);
if (sig_pipe[0] == -1)
{
if (pipe(sig_pipe) != 0)
@ -239,6 +250,7 @@ _ecore_signal_pipe_shutdown(void)
efl_del(sig_pipe_handler);
sig_pipe_handler = NULL;
}
eina_spinlock_free(&sig_pid_lock);
}
static void
@ -279,6 +291,45 @@ _ecore_signal_count_get(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
return 0;
}
void
_ecore_signal_pid_lock(void)
{
eina_spinlock_take(&sig_pid_lock);
}
void
_ecore_signal_pid_unlock(void)
{
eina_spinlock_release(&sig_pid_lock);
}
void
_ecore_signal_pid_register(pid_t pid, int fd)
{
Pid_Info *pi = calloc(1, sizeof(Pid_Info));
if (!pi) return;
pi->pid = pid;
pi->fd = fd;
sig_pid_info_list = eina_list_append(sig_pid_info_list, pi);
}
void
_ecore_signal_pid_unregister(pid_t pid, int fd)
{
Eina_List *l;
Pid_Info *pi;
EINA_LIST_FOREACH(sig_pid_info_list, l, pi)
{
if ((pi->pid == pid) && (pi->fd == fd))
{
sig_pid_info_list = eina_list_remove_list(sig_pid_info_list, l);
free(pi);
return;
}
}
}
static void
_ecore_signal_exe_exit_delay(void *data, const Efl_Event *event)
{
@ -356,6 +407,45 @@ _ecore_signal_waitpid(Eina_Bool once, siginfo_t info)
else ecore_event_add(ECORE_EXE_EVENT_DEL, e,
_ecore_exe_event_del_free, NULL);
}
// XXX: this is not brilliant. this ends up running from the main loop
// reading the signal pipe to handle signals. that means handling
// exe exits from children will be bottlenecked by how often
// the main loop can wake up (or well latency may not be great).
// this should probably have a dedicated thread ythat does a waitpid()
// and blocks and waits sending results to the resulting pipe
Eina_List *l, *ll;
Pid_Info *pi;
EINA_LIST_FOREACH_SAFE(sig_pid_info_list, l, ll, pi)
{
if (pi->pid == pid)
{
Ecore_Signal_Pid_Info pinfo;
sig_pid_info_list = eina_list_remove_list
(sig_pid_info_list, ll);
pinfo.pid = pid;
pinfo.info = info;
if (WIFEXITED(status))
{
pinfo.exit_code = WEXITSTATUS(status);
pinfo.exit_signal = -1;
}
else if (WIFSIGNALED(status))
{
pinfo.exit_code = -1;
pinfo.exit_signal = WTERMSIG(status);
}
if (write(pi->fd, &pinfo, sizeof(Ecore_Signal_Pid_Info))
!= sizeof(Ecore_Signal_Pid_Info))
{
ERR("Can't write to custom exe exit info pipe");
}
free(pi);
break;
}
}
if (once) break;
}
}

756
src/lib/ecore/efl_exe.c Normal file
View File

@ -0,0 +1,756 @@
#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"
#ifdef _WIN32
#else
# include <sys/time.h>
# include <sys/resource.h>
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
# include <errno.h>
# include <sys/types.h>
# include <unistd.h>
# include <fcntl.h>
# include <signal.h>
# include <sys/socket.h>
# ifdef HAVE_PRCTL
# include <sys/prctl.h>
# endif
# ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
# endif
#endif
#define MY_CLASS EFL_EXE_CLASS
typedef struct _Efl_Exe_Data Efl_Exe_Data;
struct _Efl_Exe_Data
{
int exit_signal;
#ifdef _WIN32
struct {
Eo *in_handler, *out_handler;
Eina_Bool can_read : 1;
Eina_Bool eos_read : 1;
Eina_Bool can_write : 1;
} fd;
#else
Eo *exit_handler;
pid_t pid;
struct {
int in, out, exited_read, exited_write;
Eo *in_handler, *out_handler;
Eina_Bool can_read : 1;
Eina_Bool eos_read : 1;
Eina_Bool can_write : 1;
} fd;
#endif
Eina_Bool exit_called : 1;
Efl_Exe_Flags flags;
};
//////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
#else
static const signed char primap[EFL_TASK_PRIORITY_ULTRA + 1] =
{
10, // EFL_TASK_PRIORITY_NORMAL
19, // EFL_TASK_PRIORITY_BACKGROUND
15, // EFL_TASK_PRIORITY_LOW
5, // EFL_TASK_PRIORITY_HIGH
0 // EFL_TASK_PRIORITY_ULTRA
};
static void
_close_fds(Efl_Exe_Data *pd)
{
if (pd->fd.in >= 0) close(pd->fd.in);
if (pd->fd.out >= 0) close(pd->fd.out);
if (pd->fd.exited_read >= 0) close(pd->fd.exited_read);
if (pd->fd.exited_write >= 0) close(pd->fd.exited_write);
pd->fd.in = -1;
pd->fd.out = -1;
pd->fd.exited_read = -1;
pd->fd.exited_write = -1;
}
static void
_exec(const char *cmd, Efl_Exe_Flags flags)
{
char use_sh = 1, *buf = NULL, **args = NULL;
// Try to avoid wrapping the exe call with /bin/sh -c.
// We conservatively search for certain shell meta characters,
// If we don't find them, we can call the exe directly.
if (!strpbrk(cmd, "|&;<>()$\\\"'*?#"))
{
char *token, pre_command = 1;
int num_tokens = 0, len;
len = strlen(cmd);
buf = alloca(len + 1);
strcpy(buf, cmd);
buf[len] = 0;
token = strtok(buf, " \t\n\v");
while (token)
{
if (token[0] == '~') break;
if (pre_command)
{
if (token[0] == '[') break;
if (strchr(token, '=')) break;
else pre_command = 0;
}
num_tokens++;
token = strtok(NULL, " \t\n\v");
}
if ((!token) && (num_tokens))
{
int i = 0;
len = strlen(cmd);
buf = alloca(len + 1);
strcpy(buf, cmd);
buf[len] = 0;
token = strtok(buf, " \t\n\v");
use_sh = 0;
args = alloca((num_tokens + 1) * sizeof(char *));
for (i = 0; i < num_tokens; i++)
{
if (token) args[i] = token;
token = strtok(NULL, " \t\n\v");
}
args[num_tokens] = NULL;
}
}
# ifdef HAVE_PRCTL
if (flags & EFL_EXE_FLAGS_EXIT_WITH_PARENT)
prctl(PR_SET_PDEATHSIG, SIGTERM);
# endif
if (flags & EFL_EXE_FLAGS_GROUP_LEADER) setsid();
if (use_sh) // We have to use a shell to run this.
{
errno = 0;
execl("/bin/sh", "/bin/sh", "-c", cmd, NULL);
}
else
{ // We can run this directly.
if (!args)
{
ERR("arg[0] is NULL!");
return;
}
errno = 0;
if (args[0]) execvp(args[0], args);
}
}
static Eina_Bool
_foreach_env(const Eina_Hash *hash EINA_UNUSED, const void *key, void *data, void *fdata EINA_UNUSED)
{
int keylen;
char *buf, *s;
if (!data) return EINA_TRUE;
keylen = strlen(key);
buf = alloca(keylen + 1 + strlen(data) + 1);
strcpy(buf, key);
buf[keylen] = '=';
strcpy(buf + keylen + 1, data);
if ((s = strdup(buf))) putenv(s);
return EINA_TRUE;
}
static void
_exe_exit_eval(Eo *obj, Efl_Exe_Data *pd)
{
// XXX: defer the below in a job
if ((pd->fd.out == -1) && (pd->fd.exited_read == -1) &&
(!pd->exit_called))
{
pd->exit_called = EINA_TRUE;
efl_event_callback_call(obj, EFL_TASK_EVENT_EXIT, NULL);
}
}
static void
_cb_exe_exit_read(void *data, const Efl_Event *event EINA_UNUSED)
{
Eo *obj = data;
Efl_Exe_Data *pd = efl_data_scope_get(obj, MY_CLASS);
Ecore_Signal_Pid_Info pinfo;
if (!pd) return;
if (read(pd->fd.exited_read, &pinfo, sizeof(Ecore_Signal_Pid_Info)) ==
sizeof(Ecore_Signal_Pid_Info))
{
Efl_Task_Data *td = efl_data_scope_get(obj, EFL_TASK_CLASS);
if (td)
{
td->exited = EINA_TRUE;
td->exit_code = pinfo.exit_code;
pd->exit_signal = pinfo.exit_signal;
}
}
// we don't need this fd and handler anymore now we code exit status
close(pd->fd.exited_read);
pd->fd.exited_read = -1;
efl_del(pd->exit_handler);
pd->exit_handler = NULL;
_exe_exit_eval(obj, pd);
// XXX: autodel of object here is the sensible easy thing to do in C
// because then you can just run exe's and not have to listen to them exit
// and do your own del every time - they will then not leak and just
// self-cleanup without needing a del of the obj on run. but other
// languages don't like this, so if you dont care to listen to end/death
// and then del/unref the obj there... always del/unref it immediately.
}
static void
_cb_exe_out(void *data, const Efl_Event *event EINA_UNUSED)
{
Eo *obj = data;
efl_io_reader_can_read_set(obj, EINA_TRUE);
}
static void
_cb_exe_in(void *data, const Efl_Event *event EINA_UNUSED)
{
Eo *obj = data;
efl_io_writer_can_write_set(obj, EINA_TRUE);
}
#endif
//////////////////////////////////////////////////////////////////////////
EOLIAN static void
_efl_exe_signal(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd, Efl_Exe_Signal sig)
{
#ifdef _WIN32
#else
int s = 0;
if (pd->pid == -1) return;
switch (sig)
{
case EFL_EXE_SIGNAL_INT: s = SIGINT; break;
case EFL_EXE_SIGNAL_QUIT: s = SIGQUIT; break;
case EFL_EXE_SIGNAL_TERM: s = SIGTERM; break;
case EFL_EXE_SIGNAL_KILL: s = SIGKILL; break;
case EFL_EXE_SIGNAL_CONT: s = SIGCONT; break;
case EFL_EXE_SIGNAL_STOP: s = SIGSTOP; break;
case EFL_EXE_SIGNAL_HUP: s = SIGHUP; break;
case EFL_EXE_SIGNAL_USR1: s = SIGUSR1; break;
case EFL_EXE_SIGNAL_USR2: s = SIGUSR2; break;
default: return;
}
kill(pd->pid, s);
#endif
}
EOLIAN static void
_efl_exe_flags_set(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd, Efl_Exe_Flags flags)
{
pd->flags = flags;
}
EOLIAN static Efl_Exe_Flags
_efl_exe_flags_get(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
{
return pd->flags;
}
EOLIAN static void
_efl_exe_efl_task_priority_set(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd, Efl_Task_Priority priority)
{
efl_task_priority_set(efl_super(obj, MY_CLASS), priority);
#ifdef _WIN32
#else
int p = 0;
if (pd->pid != -1)
{
if ((priority >= EFL_TASK_PRIORITY_NORMAL) &&
(priority <= EFL_TASK_PRIORITY_ULTRA))
p = primap[priority];
}
setpriority(PRIO_PROCESS, pd->pid, p);
#endif
}
EOLIAN static Efl_Task_Priority
_efl_exe_efl_task_priority_get(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
{
Efl_Task_Priority pri = EFL_TASK_PRIORITY_NORMAL;
#ifdef _WIN32
#else
int p, i, dist = 0x7fffffff, d;
if (pd->pid == -1)
return efl_task_priority_get(efl_super(obj, MY_CLASS));
// p is -20 -> 19
errno = 0;
p = getpriority(PRIO_PROCESS, pd->pid);
if (errno != 0)
return efl_task_priority_get(efl_super(obj, MY_CLASS));
// find the closest matching priority in primap
for (i = EFL_TASK_PRIORITY_NORMAL; i <= EFL_TASK_PRIORITY_ULTRA; i++)
{
d = primap[i] - p;
if (d < 0) d = -d;
if (d < dist)
{
pri = i;
dist = d;
}
}
Efl_Task_Data *td = efl_data_scope_get(obj, EFL_TASK_CLASS);
if (td) td->priority = pri;
#endif
return pri;
}
EOLIAN static Eina_Bool
_efl_exe_efl_task_run(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
{
#ifdef _WIN32
return EINA_FALSE;
#else
Eo *loop;
Efl_Task_Data *tdl, *td = efl_data_scope_get(obj, EFL_TASK_CLASS);
const char *cmd;
int devnull;
int pipe_stdin[2];
int pipe_stdout[2];
int pipe_exited[2];
if (pd->pid != -1) return EINA_FALSE;
if (!td) return EINA_FALSE;
// get a cmdline to run
cmd = efl_task_command_get(obj);
if (!cmd) return EINA_FALSE;
pipe(pipe_exited);
pd->fd.exited_read = pipe_exited[0];
eina_file_close_on_exec(pd->fd.exited_write, EINA_TRUE);
pd->fd.exited_write = pipe_exited[1];
eina_file_close_on_exec(pd->fd.exited_read, EINA_TRUE);
if (pd->flags & EFL_EXE_FLAGS_USE_STDIN)
{
pipe(pipe_stdin);
pd->fd.in = pipe_stdin[1];
fcntl(pd->fd.in, F_SETFL, O_NONBLOCK);
eina_file_close_on_exec(pd->fd.in, EINA_TRUE);
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_exe_in, obj));
}
if (pd->flags & EFL_EXE_FLAGS_USE_STDOUT)
{
pipe(pipe_stdout);
pd->fd.out = pipe_stdout[0];
fcntl(pd->fd.out, F_SETFL, O_NONBLOCK);
eina_file_close_on_exec(pd->fd.out, EINA_TRUE);
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_exe_out, obj),
efl_loop_handler_active_set
(efl_added, EFL_LOOP_HANDLER_FLAGS_READ));
}
_ecore_signal_pid_lock();
pd->pid = fork();
if (pd->pid != 0)
{
// parent process is here inside this if block
if (pd->flags & EFL_EXE_FLAGS_USE_STDIN) close(pipe_stdin[0]);
if (pd->flags & EFL_EXE_FLAGS_USE_STDOUT) close(pipe_stdout[1]);
// fork failed... close up and clean and release locks
if (pd->pid == -1)
{
_close_fds(pd);
_ecore_signal_pid_unlock();
return EINA_FALSE;
}
// register this pid in the core sigchild/pid exit code watcher
_ecore_signal_pid_register(pd->pid, pd->fd.exited_write);
pd->exit_handler =
efl_add(EFL_LOOP_HANDLER_CLASS, obj,
efl_loop_handler_fd_set(efl_added, pd->fd.exited_read),
efl_event_callback_add(efl_added,
EFL_LOOP_HANDLER_EVENT_READ,
_cb_exe_exit_read, obj),
efl_loop_handler_active_set(efl_added,
EFL_LOOP_HANDLER_FLAGS_READ));
_ecore_signal_pid_unlock();
return EINA_TRUE;
}
// this code is in the child here, and is temporary setup until we
// exec() the child to replace everything.
if (pd->flags & EFL_EXE_FLAGS_USE_STDIN) close(pipe_stdin[1]);
if (pd->flags & EFL_EXE_FLAGS_USE_STDOUT) close(pipe_stdout[0]);
// set priority of self
if ((td->priority >= EFL_TASK_PRIORITY_NORMAL) &&
(td->priority <= EFL_TASK_PRIORITY_ULTRA))
setpriority(PRIO_PROCESS, 0, primap[td->priority]);
// if we want to hide or use any of the stdio, close the fd's
if ((pd->flags & EFL_EXE_FLAGS_USE_STDIN) ||
(pd->flags & EFL_EXE_FLAGS_HIDE_IO))
close(STDIN_FILENO);
if ((pd->flags & EFL_EXE_FLAGS_USE_STDOUT) ||
(pd->flags & EFL_EXE_FLAGS_HIDE_IO))
close(STDOUT_FILENO);
if ((pd->flags & EFL_EXE_FLAGS_HIDE_IO))
close(STDERR_FILENO);
if (!(pd->flags & EFL_EXE_FLAGS_USE_STDIN) &&
(pd->flags & EFL_EXE_FLAGS_HIDE_IO))
{
// hide stdin
devnull = open("/dev/null", O_RDONLY);
dup2(devnull, STDIN_FILENO);
close(devnull);
}
else if ((pd->flags & EFL_EXE_FLAGS_USE_STDIN))
{
// hook up stdin to the pipe going to the parent
dup2(pipe_stdin[0], STDIN_FILENO);
close(pipe_stdin[0]);
}
if (!(pd->flags & EFL_EXE_FLAGS_USE_STDOUT) &&
(pd->flags & EFL_EXE_FLAGS_HIDE_IO))
{
// hide stdout
devnull = open("/dev/null", O_WRONLY);
dup2(devnull, STDOUT_FILENO);
close(devnull);
}
else if ((pd->flags & EFL_EXE_FLAGS_USE_STDOUT))
{
// hook up stdout to the pipe going to the parent
dup2(pipe_stdout[1], STDOUT_FILENO);
close(pipe_stdout[1]);
}
if ((pd->flags & EFL_EXE_FLAGS_HIDE_IO))
{
// hide stderr
devnull = open("/dev/null", O_WRONLY);
dup2(devnull, STDERR_FILENO);
close(devnull);
}
if (!(loop = efl_provider_find(obj, EFL_LOOP_CLASS))) exit(-120);
if (!(tdl = efl_data_scope_get(loop, EFL_TASK_CLASS))) exit(-121);
// clear systemd notify socket... only relevant for systemd world,
// otherwise shouldn't be trouble
putenv("NOTIFY_SOCKET=");
// force the env hash to update from env vars
efl_task_env_get(loop, "HOME");
// actually setenv the env hash (clear what was there before so it is
// the only env there)
clearenv();
eina_hash_foreach(td->env, _foreach_env, NULL);
// actually execute!
_exec(cmd, pd->flags);
// we couldn't exec... uh oh. HAAAAAAAALP!
exit(-122);
return EINA_FALSE;
#endif
}
EOLIAN static void
_efl_exe_efl_task_end(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
{
#ifdef _WIN32
#else
if (pd->pid == -1) return;
kill(pd->pid, SIGINT);
#endif
}
EOLIAN static int
_efl_exe_exit_signal_get(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
{
return pd->exit_signal;
}
EOLIAN static Efl_Object *
_efl_exe_efl_object_constructor(Eo *obj, Efl_Exe_Data *pd)
{
obj = efl_constructor(efl_super(obj, MY_CLASS));
#ifdef _WIN32
#else
pd->pid = -1;
pd->fd.in = -1;
pd->fd.out = -1;
pd->fd.exited_read = -1;
#endif
pd->fd.can_write = EINA_TRUE;
pd->flags = EFL_EXE_FLAGS_EXIT_WITH_PARENT;
pd->exit_signal = -1;
return obj;
}
EOLIAN static void
_efl_exe_efl_object_destructor(Eo *obj, Efl_Exe_Data *pd)
{
#ifdef _WIN32
#else
if (pd->fd.exited_read >= 0)
{
_ecore_signal_pid_lock();
_ecore_signal_pid_unregister(pd->pid, pd->fd.exited_read);
_ecore_signal_pid_unlock();
close(pd->fd.exited_read);
pd->fd.exited_read = -1;
efl_del(pd->exit_handler);
pd->exit_handler = NULL;
}
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_handler = NULL;
pd->fd.out_handler = NULL;
_close_fds(pd);
#endif
efl_destructor(efl_super(obj, MY_CLASS));
}
EOLIAN static Eina_Error
_efl_exe_efl_io_closer_close(Eo *obj, Efl_Exe_Data *pd)
{
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);
#ifdef _WIN32
#else
if (pd->fd.in >= 0) close(pd->fd.in);
if (pd->fd.out >= 0) close(pd->fd.out);
if (pd->fd.exited_read >= 0) close(pd->fd.exited_read);
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.exited_read = -1;
pd->fd.in_handler = NULL;
pd->fd.out_handler = NULL;
#endif
return 0;
}
EOLIAN static Eina_Bool
_efl_exe_efl_io_closer_closed_get(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
{
#ifdef _WIN32
return EINA_FALSE;
#else
if ((pd->fd.in == -1) && (pd->fd.out == -1)) return EINA_TRUE;
#endif
return EINA_FALSE;
}
EOLIAN static Eina_Error
_efl_exe_efl_io_reader_read(Eo *obj, Efl_Exe_Data *pd, Eina_Rw_Slice *rw_slice)
{
#ifdef _WIN32
return EINVAL;
#else
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;
_exe_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);
_exe_exit_eval(obj, pd);
return EINVAL;
#endif
}
EOLIAN static void
_efl_exe_efl_io_reader_can_read_set(Eo *obj, Efl_Exe_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 (can_read)
efl_loop_handler_active_set(pd->fd.in_handler, 0);
else
efl_loop_handler_active_set(pd->fd.in_handler,
EFL_LOOP_HANDLER_FLAGS_READ);
efl_event_callback_call(obj, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
}
EOLIAN static Eina_Bool
_efl_exe_efl_io_reader_can_read_get(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
{
return pd->fd.can_read;
}
EOLIAN static void
_efl_exe_efl_io_reader_eos_set(Eo *obj, Efl_Exe_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_exe_efl_io_reader_eos_get(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
{
return pd->fd.eos_read;
}
EOLIAN static Eina_Error
_efl_exe_efl_io_writer_write(Eo *obj, Efl_Exe_Data *pd, Eina_Slice *slice, Eina_Slice *remaining)
{
#ifdef _WIN32
return EINVAL;
#else
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;
_exe_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);
_exe_exit_eval(obj, pd);
return EINVAL;
#endif
}
EOLIAN static void
_efl_exe_efl_io_writer_can_write_set(Eo *obj, Efl_Exe_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_exe_efl_io_writer_can_write_get(Eo *obj EINA_UNUSED, Efl_Exe_Data *pd)
{
return pd->fd.can_write;
}
//////////////////////////////////////////////////////////////////////////
#include "efl_exe.eo.c"

65
src/lib/ecore/efl_exe.eo Normal file
View File

@ -0,0 +1,65 @@
import efl_types;
import eina_types;
enum Efl.Exe.Signal {
[[ ]]
int,
quit,
term,
kill,
cont,
stop,
hup,
usr1,
usr2
}
enum Efl.Exe.Flags {
[[ ]]
none = 0,
group_leader = 1,
exit_with_parent = 2,
hide_io = 4,
use_stdin = 8,
use_stdout = 16,
}
class Efl.Exe (Efl.Task)
{
[[ ]]
methods {
signal {
params {
sig: Efl.Exe.Signal; [[ Send this signal to the task ]]
}
}
@property flags {
set { }
get { }
values {
flags: Efl.Exe.Flags; [[ ]]
}
}
@property exit_signal {
[[ The final exit signal of this task. ]]
get { }
values {
sig: int; [[ The exit signal, or -1 if no exit signal happened ]]
}
}
}
implements {
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Task.priority { get; set; }
Efl.Task.run;
Efl.Task.end;
Efl.Io.Closer.close;
Efl.Io.Closer.closed { get; }
Efl.Io.Reader.read;
Efl.Io.Reader.can_read { get; set; }
Efl.Io.Reader.eos { get; set; }
Efl.Io.Writer.write;
Efl.Io.Writer.can_write { get; set; }
}
}

View File

@ -7,12 +7,19 @@
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <sys/time.h>
#ifndef _WIN32
# include <sys/resource.h>
#endif
#include <errno.h>
#include "Ecore.h"
#include "ecore_private.h"
#include "ecore_main_common.h"
extern char **environ;
typedef struct _Efl_Loop_Promise_Simple_Data Efl_Loop_Promise_Simple_Data;
typedef struct _Efl_Internal_Promise Efl_Internal_Promise;
@ -51,6 +58,11 @@ Efl_Version _app_efl_version = { 0, 0, 0, 0, NULL, NULL };
Eo *_mainloop_singleton = NULL;
Efl_Loop_Data *_mainloop_singleton_data = NULL;
extern Eina_Lock _environ_lock;
static Eina_List *_environ_strings_set = NULL;
static void _clean_old_environ(void);
EOLIAN static Efl_Loop *
_efl_loop_main_get(Efl_Class *klass EINA_UNUSED, void *_pd EINA_UNUSED)
{
@ -345,6 +357,14 @@ _efl_loop_efl_object_destructor(Eo *obj, Efl_Loop_Data *pd)
pd->message_handlers = NULL;
}
eina_lock_take(&_environ_lock);
_clean_old_environ();
_environ_strings_set = eina_list_free(_environ_strings_set);
pd->env.environ_ptr = NULL;
free(pd->env.environ_copy);
pd->env.environ_copy = NULL;
eina_lock_release(&_environ_lock);
efl_destructor(efl_super(obj, EFL_LOOP_CLASS));
if (obj == _mainloop_singleton)
@ -395,9 +415,13 @@ ecore_loop_arguments_send(int argc, const char **argv)
Eina_Array *arga;
int i = 0;
efl_task_arg_reset(efl_main_loop_get());
arga = eina_array_new(argc);
for (i = 0; i < argc; i++)
eina_array_push(arga, eina_stringshare_add(argv[i]));
{
eina_array_push(arga, eina_stringshare_add(argv[i]));
efl_task_arg_append(efl_main_loop_get(), argv[i]);
}
job = eina_future_then(efl_loop_job(efl_main_loop_get()),
_efl_loop_arguments_send, arga);
@ -722,6 +746,222 @@ _efl_loop_efl_version_get(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
return &version;
}
static void
_env_sync(Efl_Loop_Data *pd, Efl_Task_Data *td)
{
Eina_Bool update = EINA_FALSE;
unsigned int count = 0, i;
char **p;
// count environs
if (environ)
{
for (p = environ; *p; p++) count++;
}
// cached env ptr is the same... so look deeper if things changes
if (pd->env.environ_ptr == environ)
{
// if we have no cached copy then update
if (!pd->env.environ_copy) update = EINA_TRUE;
else
{
// if any ptr in the cached copy doesnt match environ ptr
// then update
for (i = 0; i <= count; i++)
{
if (pd->env.environ_copy[i] != environ[i])
{
printf(" env %i mismatch\n", i);
update = EINA_TRUE;
break;
}
}
}
}
// cached env ptr changed so we need to update anyway
else update = EINA_TRUE;
if (!update) return;
// things changed - do the update
pd->env.environ_ptr = environ;
free(pd->env.environ_copy);
pd->env.environ_copy = NULL;
if (count > 0)
{
pd->env.environ_copy = malloc((count + 1) * sizeof(char *));
if (pd->env.environ_copy)
{
for (i = 0; i <= count; i++)
pd->env.environ_copy[i] = environ[i];
}
}
// clear previous env hash and rebuild it from environ so it matches
if (td->env) eina_hash_free(td->env);
td->env = eina_hash_string_superfast_new
((Eina_Free_Cb)eina_stringshare_del);
for (i = 0; i < count; i++)
{
char *var;
const char *value;
if (!environ[i]) continue;
if ((value = strchr(environ[i], '=')))
{
if (*value)
{
if ((var = malloc(value - environ[i] + 1)))
{
strncpy(var, environ[i], value - environ[i]);
var[value - environ[i]] = 0;
value++;
eina_hash_add(td->env, var,
eina_stringshare_add(value));
free(var);
}
}
}
}
}
static void
_clean_old_environ(void)
{
char **p;
const char *str;
Eina_List *l, *ll;
Eina_Bool ok;
// clean up old strings no longer in environ
EINA_LIST_FOREACH_SAFE(_environ_strings_set, l, ll, str)
{
ok = EINA_FALSE;
for (p = environ; *p; p++)
{
if (*p == str)
{
ok = EINA_TRUE;
break;
}
}
if (!ok)
{
_environ_strings_set =
eina_list_remove_list(_environ_strings_set, l);
eina_stringshare_del(str);
}
}
}
EOLIAN static void
_efl_loop_efl_task_env_set(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, const char *var, const char *value)
{
char *str, *str2;
size_t varlen, vallen = 0;
if (!var) return;
Efl_Task_Data *td = efl_data_scope_get(obj, EFL_TASK_CLASS);
if (!td) return;
varlen = strlen(var);
if (value) vallen = strlen(value);
str = malloc(varlen + 1 + vallen + 1);
if (!str) return;
strcpy(str, var);
str[varlen] = '=';
if (value) strcpy(str + varlen + 1, value);
else str[varlen + 1] = 0;
str2 = (char *)eina_stringshare_add(str);
free(str);
if (!str2) return;
eina_lock_take(&_environ_lock);
if (putenv(str2) != 0)
{
eina_stringshare_del(str2);
eina_lock_release(&_environ_lock);
return;
}
_environ_strings_set = eina_list_append(_environ_strings_set, str2);
eina_lock_release(&_environ_lock);
efl_task_env_set(efl_super(obj, EFL_LOOP_CLASS), var, value);
eina_lock_take(&_environ_lock);
_clean_old_environ();
eina_lock_release(&_environ_lock);
}
EOLIAN static const char *
_efl_loop_efl_task_env_get(Eo *obj, Efl_Loop_Data *pd, const char *var)
{
Efl_Task_Data *td = efl_data_scope_get(obj, EFL_TASK_CLASS);
if (!td) return NULL;
eina_lock_take(&_environ_lock);
_env_sync(pd, td);
eina_lock_release(&_environ_lock);
return efl_task_env_get(efl_super(obj, EFL_LOOP_CLASS), var);
}
#ifdef _WIN32
#else
static const signed char primap[EFL_TASK_PRIORITY_ULTRA + 1] =
{
10, // EFL_TASK_PRIORITY_NORMAL
19, // EFL_TASK_PRIORITY_BACKGROUND
15, // EFL_TASK_PRIORITY_LOW
5, // EFL_TASK_PRIORITY_HIGH
0 // EFL_TASK_PRIORITY_ULTRA
};
#endif
EOLIAN static void
_efl_loop_efl_task_priority_set(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, Efl_Task_Priority priority)
{
efl_task_priority_set(efl_super(obj, EFL_LOOP_CLASS), priority);
#ifdef _WIN32
#else
// -20 (high) -> 19 (low)
int p = 0;
if ((priority >= EFL_TASK_PRIORITY_NORMAL) &&
(priority <= EFL_TASK_PRIORITY_ULTRA))
p = primap[priority];
setpriority(PRIO_PROCESS, 0, p);
#endif
}
EOLIAN static Efl_Task_Priority
_efl_loop_efl_task_priority_get(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
{
Efl_Task_Priority pri = EFL_TASK_PRIORITY_NORMAL;
#ifdef _WIN32
#else
int p, i, dist = 0x7fffffff, d;
errno = 0;
p = getpriority(PRIO_PROCESS, 0);
if (errno != 0)
return efl_task_priority_get(efl_super(obj, EFL_LOOP_CLASS));
// find the closest matching priority in primap
for (i = EFL_TASK_PRIORITY_NORMAL; i <= EFL_TASK_PRIORITY_ULTRA; i++)
{
d = primap[i] - p;
if (d < 0) d = -d;
if (d < dist)
{
pri = i;
dist = d;
}
}
Efl_Task_Data *td = efl_data_scope_get(obj, EFL_TASK_CLASS);
if (td) td->priority = pri;
#endif
return pri;
}
EAPI Eina_Future_Scheduler *
efl_loop_future_scheduler_get(const Eo *obj)
{

View File

@ -7,7 +7,7 @@ struct Efl.Loop.Arguments {
initialization: bool; [[Set to $true when the program should initialize its internal state. This happen once per process instance.]]
}
class Efl.Loop (Efl.Object)
class Efl.Loop (Efl.Task)
{
[[The Efl Main Loop
@ -159,5 +159,7 @@ class Efl.Loop (Efl.Object)
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Object.provider_find;
Efl.Task.env { set; get; }
Efl.Task.priority { get; set; }
}
}

417
src/lib/ecore/efl_task.c Normal file
View File

@ -0,0 +1,417 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
#define MY_CLASS EFL_TASK_CLASS
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
static void
_clear_args(Efl_Task_Data *pd)
{
unsigned int count, i;
if (!pd->args) return;
count = eina_array_count(pd->args);
for (i = 0; i < count; i++)
eina_stringshare_del(eina_array_data_get(pd->args, i));
eina_array_free(pd->args);
pd->args = NULL;
}
static Eina_Array *
_unescape(const char *s)
{
Eina_Array *args;
const char *p;
char *tmp = NULL, *d = NULL;
if (!s) return NULL;
Eina_Bool in_quote_dbl = EINA_FALSE;
Eina_Bool in_quote = EINA_FALSE;
args = eina_array_new(16);
if (!args) return NULL;
for (p = s; *p; p++)
{
if (!tmp) tmp = d = strdup(p);
if (tmp)
{
if (in_quote_dbl)
{
switch (*p)
{
case '\"':
in_quote_dbl = EINA_FALSE;
*d = 0;
eina_array_push(args, eina_stringshare_add(tmp));
free(tmp);
tmp = d = NULL;
break;
case '\\':
p++;
EINA_FALLTHROUGH
default:
*d = *p;
d++;
break;
}
}
else if (in_quote)
{
switch (*p)
{
case '\'':
in_quote = EINA_FALSE;
*d = 0;
eina_array_push(args, eina_stringshare_add(tmp));
free(tmp);
tmp = d = NULL;
break;
case '\\':
p++;
EINA_FALLTHROUGH
default:
*d = *p;
d++;
break;
}
}
else
{
switch (*p)
{
case ' ':
case '\t':
case '\r':
case '\n':
*d = 0;
eina_array_push(args, eina_stringshare_add(tmp));
free(tmp);
tmp = d = NULL;
break;
case '\"':
in_quote_dbl = EINA_TRUE;
break;
case '\'':
in_quote = EINA_TRUE;
break;
case '\\':
p++;
EINA_FALLTHROUGH
default:
*d = *p;
d++;
break;
}
}
}
}
if (tmp)
{
*d = 0;
eina_array_push(args, eina_stringshare_add(tmp));
free(tmp);
}
return args;
}
static char *
_escape(const char *s)
{
Eina_Bool need_quote = EINA_FALSE;
const char *p;
char *s2 = malloc((strlen(s) * 2) + 1 + 2), *d;
if (!s2) return NULL;
for (p = s; *p; p++)
{
switch (*p)
{
case '\'':
case '\"':
case '$':
case '#':
case ';':
case '&':
case '`':
case '|':
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '>':
case '<':
case '\n':
case '\r':
case '\t':
need_quote = EINA_TRUE;
default:
break;
}
}
d = s2;
if (need_quote)
{
*d = '\"';
d++;
}
for (p = s; *p; p++, d++)
{
switch (*p)
{
case ' ':
case '\\':
case '\'':
case '\"':
*d = '\\';
d++;
EINA_FALLTHROUGH
default:
*d = *p;
break;
}
}
if (need_quote)
{
*d = '\"';
d++;
*d = 0;
}
return s2;
}
static void
_rebuild_command(Efl_Task_Data *pd)
{
unsigned int count, i;
Eina_Strbuf *sb;
const char *arg, *cmd;
Eina_Bool have_args = EINA_FALSE;
if (!pd->command_dirty) return;
pd->command_dirty = EINA_FALSE;
eina_stringshare_del(pd->command);
pd->command = NULL;
if (!pd->args) return;
sb = eina_strbuf_new();
if (!sb) return;
count = eina_array_count(pd->args);
for (i = 0; i < count; i++)
{
arg = eina_array_data_get(pd->args, i);
if (arg)
{
char *str = _escape(arg);
if (str)
{
if (!have_args) eina_strbuf_append(sb, " ");
eina_strbuf_append(sb, str);
free(str);
have_args = EINA_TRUE;
}
}
}
cmd = eina_strbuf_string_get(sb);
if (cmd) pd->command = eina_stringshare_add(cmd);
eina_strbuf_free(sb);
}
static Eina_Bool
_foreach_env_copy(const Eina_Hash *hash EINA_UNUSED, const void *key, void *data, void *fdata)
{
if (data)
{
// only copy env vars not already set
if (!eina_hash_find(fdata, key))
eina_hash_add(fdata, key, eina_stringshare_add(data));
}
return EINA_TRUE;
}
//////////////////////////////////////////////////////////////////////////
EOLIAN static void
_efl_task_command_set(Eo *obj EINA_UNUSED, Efl_Task_Data *pd, const char *command)
{
eina_stringshare_replace(&pd->command, command);
_clear_args(pd);
pd->args = _unescape(pd->command);
}
EOLIAN static const char *
_efl_task_command_get(Eo *obj EINA_UNUSED, Efl_Task_Data *pd)
{
_rebuild_command(pd);
return pd->command;
}
EOLIAN static unsigned int
_efl_task_arg_count_get(Eo *obj EINA_UNUSED, Efl_Task_Data *pd)
{
if (!pd->args) return 0;
return eina_array_count(pd->args);
}
EOLIAN static void
_efl_task_arg_value_set(Eo *obj EINA_UNUSED, Efl_Task_Data *pd, unsigned int num, const char *arg)
{
const char *parg = NULL;
unsigned int count;
if (!pd->args) pd->args = eina_array_new(16);
count = eina_array_count(pd->args);
if (count > num)
parg = eina_array_data_get(pd->args, num);
else
{
unsigned int i;
for (i = count; i <= num; i++)
eina_array_push(pd->args, NULL);
}
if (arg)
eina_array_data_set(pd->args, num, eina_stringshare_add(arg));
else
eina_array_data_set(pd->args, num, NULL);
if (parg) eina_stringshare_del(parg);
pd->command_dirty = EINA_TRUE;
}
EOLIAN static const char *
_efl_task_arg_value_get(Eo *obj EINA_UNUSED, Efl_Task_Data *pd, unsigned int num)
{
unsigned int count;
if (!pd->args) return NULL;
count = eina_array_count(pd->args);
if (num >= count) return NULL;
return eina_array_data_get(pd->args, num);
}
EOLIAN static void
_efl_task_arg_append(Eo *obj EINA_UNUSED, Efl_Task_Data *pd, const char *arg)
{
if (!pd->args) pd->args = eina_array_new(16);
if (arg)
eina_array_push(pd->args, eina_stringshare_add(arg));
else
eina_array_push(pd->args, NULL);
pd->command_dirty = EINA_TRUE;
}
EOLIAN static void
_efl_task_arg_reset(Eo *obj EINA_UNUSED, Efl_Task_Data *pd)
{
_clear_args(pd);
pd->command_dirty = EINA_TRUE;
}
EOLIAN static void
_efl_task_env_set(Eo *obj EINA_UNUSED, Efl_Task_Data *pd, const char *var, const char *value)
{
if (!var) return;
if (!pd->env)
pd->env = eina_hash_string_superfast_new
((Eina_Free_Cb)eina_stringshare_del);
if (!pd->env) return;
if ((value) && (*value))
eina_hash_add(pd->env, var, eina_stringshare_add(value));
else eina_hash_del(pd->env, var, NULL);
}
EOLIAN static const char *
_efl_task_env_get(Eo *obj EINA_UNUSED, Efl_Task_Data *pd, const char *var)
{
if ((!var) || (!pd->env)) return NULL;
return eina_hash_find(pd->env, var);
}
EOLIAN static void
_efl_task_priority_set(Eo *obj EINA_UNUSED, Efl_Task_Data *pd, Efl_Task_Priority priority)
{
pd->priority = priority;
}
EOLIAN static Efl_Task_Priority
_efl_task_priority_get(Eo *obj EINA_UNUSED, Efl_Task_Data *pd)
{
return pd->priority;
}
EOLIAN static int
_efl_task_exit_code_get(Eo *obj EINA_UNUSED, Efl_Task_Data *pd)
{
return pd->exit_code;
}
EOLIAN static Eina_Bool
_efl_task_run(Eo *obj EINA_UNUSED, Efl_Task_Data *pd EINA_UNUSED)
{
// NOP
return EINA_FALSE;
}
EOLIAN static void
_efl_task_end(Eo *obj EINA_UNUSED, Efl_Task_Data *pd EINA_UNUSED)
{
// NOP
}
EOLIAN static Efl_Object *
_efl_task_efl_object_constructor(Eo *obj, Efl_Task_Data *pd EINA_UNUSED)
{
obj = efl_constructor(efl_super(obj, MY_CLASS));
return obj;
}
EOLIAN static void
_efl_task_efl_object_destructor(Eo *obj EINA_UNUSED, Efl_Task_Data *pd)
{
eina_stringshare_del(pd->command);
pd->command = NULL;
_clear_args(pd);
if (pd->env) eina_hash_free(pd->env);
pd->env = NULL;
efl_destructor(efl_super(obj, MY_CLASS));
}
EOLIAN static void
_efl_task_efl_object_parent_set(Eo *obj, Efl_Task_Data *pd, Efl_Object *parent)
{
Eo *loop;
efl_parent_set(efl_super(obj, MY_CLASS), parent);
// copy loop env into exe task env, if not already set in env (overridden)
loop = efl_provider_find(parent, EFL_LOOP_CLASS);
if (loop)
{
Efl_Task_Data *tdl = efl_data_scope_get(loop, EFL_TASK_CLASS);
if (tdl)
{
if (!pd->env)
pd->env = eina_hash_string_superfast_new
((Eina_Free_Cb)eina_stringshare_del);
if (tdl->env)
eina_hash_foreach(tdl->env, _foreach_env_copy, pd->env);
}
}
}
//////////////////////////////////////////////////////////////////////////
#include "efl_task.eo.c"

144
src/lib/ecore/efl_task.eo Normal file
View File

@ -0,0 +1,144 @@
import efl_types;
import eina_types;
enum Efl.Task.Priority {
[[ ]]
normal,
background,
low,
high,
ultra
}
class Efl.Task (Efl.Object, Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer)
{
[[ ]]
methods {
@property command {
[[ A commandline that encodes arguments in a command string.
This command is unix shell-style, thus whitespace separates
arguments unless escaped. Also a semi-colon ';', ampersand
'&', pipe/bar '|', hash '#', bracket, square brace, brace
character ('(', ')', '[', ']', '{', '}'), exclamation
mark '!', backquote '`', greator or less than ('>' '<')
character unless escaped or in quotes would cause
args_count/value to not be generated properly, because
it would force complex shell interpretation which
will not be supported in evaluating the arg_count/value
information, but the final shell may interpret this if this
is executed via a command-line shell. To not be a complex
shell command, it should be simple with paths, options
and variable expansions, but nothing more complex involving
the above unescaped characters.
"cat -option /path/file"
"cat 'quoted argument'"
"cat ~/path/escaped\ argument"
"/bin/cat escaped\ argument $VARIABLE"
etc.
It should not try and use "complex shell features" if you
want the arg_count and arg_value set to be correct after
setting the command string. For example none of:
"VAR=x /bin/command && /bin/othercommand >& /dev/null"
"VAR=x /bin/command `/bin/othercommand` | /bin/cmd2 && cmd3 &"
etc.
If you set the command the arg_count/value property contents
can change and be completely re-evaluated by parsing the
command string into an argument array set along with
interpreting escapes back into individual argument strings. ]]
get { }
set { }
values {
command: string; [[ The command string as described ]]
}
}
@property arg_count {
[[ Number of arguments passed in or arguments that are to be
passed as sepcified by arg_value ]]
get { }
values {
args: uint; [[ ]]
}
}
@property arg_value {
[[ Argument number by index. If the index does not exist when
set, it is allocated and created. Getting an argument that
Has not been set yet will return $NULL. Empty arguments will
Be ignored. Setting an argument will result in the command
porperty being re-evaluated and escaped into a single
command string if needed. ]]
set { }
get { }
keys {
num: uint; [[ ]]
}
values {
arg: string; [[ ]]
}
}
arg_append {
[[ Append a new string argument at the end of the arg set.
This functions like setting an arg_value at the end of the
current set so the set increases by 1 in size. ]]
params {
arg: string; [[ ]]
}
}
arg_reset {
[[ Clear all arguments in arg_value/count set. Will result in the
command property also being cleared. ]]
}
@property env {
[[ The environment to be passed in or that was passed to the
task. This is a string key, value list which map to environment
variables where appropriate. The var string must contain
only an underscore ('_'), letters ('a-z', 'A-Z'),
numbers ('0-9'), but the first character may not be a number.]]
set { }
get { }
keys {
var: string; [[ The variable name as a string ]]
}
values {
value: string; [[ Set var to this value if not $NULL,
otherwise clear this env value if value
is $NULL or if it is an empty string ]]
}
}
@property priority {
[[ The priority of this task. ]]
get { }
set { }
values {
priority: Efl.Task.Priority; [[ ]]
}
}
@property exit_code {
[[ The final exit code of this task. ]]
get { }
values {
code: int; [[ ]]
}
}
run {
[[ Actually run the task ]]
return: bool; [[ True if ir started to run, false otherwise ]]
}
end {
[[ Request the task end (may send a signal or interrupt
signal resulting in a terminate event being tiggered in the
target task loop) ]]
}
}
events {
exit; [[ When the task exits and there is an exit code to get and all IO is finished and can't work anymore ]]
}
implements {
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Object.parent { set; }
}
}

View File

@ -0,0 +1,12 @@
import efl_types;
import eina_types;
class Efl.Thread (Efl.Task)
{
methods {
}
implements {
Efl.Object.constructor;
Efl.Object.destructor;
}
}