forked from enlightenment/efl
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:
parent
d7a22256b6
commit
47ff2d8126
|
@ -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 \
|
||||
|
|
|
@ -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"
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import efl_types;
|
||||
import eina_types;
|
||||
|
||||
class Efl.Thread (Efl.Task)
|
||||
{
|
||||
methods {
|
||||
}
|
||||
implements {
|
||||
Efl.Object.constructor;
|
||||
Efl.Object.destructor;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue