Ecore_exe

Creating a processes and IPC (Inter process communication)

In this example we will show how to create a new process and communicate with it in a portable way using the Ecore_exe module.

In this example we will have two process and both will communicate with each other using messages. A father process will start a child process and it will keep sending messages to the child until it receives a message to quit. To see the full source use the links:

- Father
- Child

Let's start the tutorial. The implementation of the child it's pretty simple. We just read strings from stdin and write a message in the stdout. But you should be asking yourself right know. "If I'm receiving data from an other process why I'm reading and writing in stdin/stdout?". That's because, when you spawn a process using the Ecore_Exe module it will create a pipe between the father and the child process and the stdin/stdout of the child process will be redirected to the pipe. So when the child wants to receive or send data to the father, just use the stdin/stdout. However the steps to send data from the father to the child is quite different, but we will get there.

The child will register a fd handler to monitor the stdin. So we start registering the ecore FD handler:

ecore_main_fd_handler_add(STDIN_FILENO,
ECORE_FD_READ,
_fd_handler_cb,
NULL, NULL, NULL);
ecore_main_fd_handler_add
Ecore_Fd_Handler * ecore_main_fd_handler_add(int fd, Ecore_Fd_Handler_Flags flags, Ecore_Fd_Cb func, const void *data, Ecore_Fd_Cb buf_func, const void *buf_data)
Adds a callback for activity on the given file descriptor.
Definition: ecore_main.c:1449
ECORE_FD_READ
@ ECORE_FD_READ
Fd Read mask.
Definition: Ecore_Common.h:1388

If you don't remember the parameters of ecore_main_fd_handler_add, please check its documentation.

Now that we have our handler registered we will start the ecore's main loop:

ecore_main_loop_begin();
ecore_main_loop_begin
void ecore_main_loop_begin(void)
Runs the application main loop.
Definition: ecore_main.c:1311

Now let's take a look in the callback function. Its a simple function that will read from stdin 3 times and at the third time will say to the father: "quit".

static Eina_Bool
_fd_handler_cb(void *data EINA_UNUSED, HANDLER_TYPE *fd_handler EINA_UNUSED)
{
static int numberOfMessages = 0;
char message[BUFFER_SIZE];
if (!fgets(message, BUFFER_SIZE, stdin))
return ECORE_CALLBACK_RENEW;
numberOfMessages++;
if (numberOfMessages < 3)
{
printf("My father sent this message to me:%s\n", message);
fflush(stdout);
return ECORE_CALLBACK_RENEW;
}
ECORE_CALLBACK_RENEW
#define ECORE_CALLBACK_RENEW
Return value to keep a callback.
Definition: Ecore_Common.h:153
Eina_Bool
unsigned char Eina_Bool
Type to mimic a boolean.
Definition: eina_types.h:527
EINA_UNUSED
#define EINA_UNUSED
Used to indicate that a function parameter is purposely unused.
Definition: eina_types.h:339
else
{
printf("quit\n");
fflush(stdout);
ecore_main_loop_quit();
return ECORE_CALLBACK_DONE;
}
ECORE_CALLBACK_DONE
#define ECORE_CALLBACK_DONE
Return value to stop event handling.
Definition: Ecore_Common.h:156
ecore_main_loop_quit
void ecore_main_loop_quit(void)
Quits the main loop once all the events currently on the queue have been processed.
Definition: ecore_main.c:1321
}
int
main(void)
{
if (!ecore_init())
goto error;
#ifdef _WIN32
/* note that stdin fd's on windows don't work the same
* as on unixes. this uses stdin just as a quick
* example that's simple instead of a more complex
* one, so this won't actually work on windows unless
* you use a fd that comes from somewhere that is
* select()able. */
ecore_main_win32_handler_add(GetStdHandle(STD_INPUT_HANDLE),
_fd_handler_cb,
NULL);
#else
ecore_main_fd_handler_add(STDIN_FILENO,
ECORE_FD_READ,
_fd_handler_cb,
NULL, NULL, NULL);
#endif
ecore_main_loop_begin();
ecore_shutdown();
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
ecore_main_win32_handler_add
Ecore_Win32_Handler * ecore_main_win32_handler_add(void *h, Ecore_Win32_Handle_Cb func, const void *data)
Creates a Ecore_Win32_Handler object and add it to the win32_handlers list.
Definition: ecore_main.c:1587
ecore_shutdown
EAPI int ecore_shutdown(void)
Shuts down connections, signal handlers sockets etc.
Definition: ecore.c:371
ecore_init
EAPI int ecore_init(void)
Sets up connections, signal handlers, sockets etc.
Definition: ecore.c:230

You may notice that we are sending the messages to stdout, and our father will receive it. Also our string must have a "\n" because the string will be buffered in the pipe until it finds EOF or a "newline" in our case we won't have a EOF unless we close the pipe, so we use the "\n" char.

One more thing, we use fflush(stdout) because probably our message won't fill our entire buffer and the father would never receive the message. So we use this function to flush the buffer and the father can receive as fast as possible.

Now that we have our child ready, let's start our work in the father's source code.

We start creating the child process like this:

childHandle = ecore_exe_pipe_run("./ecore_exe_example_child",
ECORE_EXE_PIPE_WRITE |
ECORE_EXE_PIPE_READ_LINE_BUFFERED |
ECORE_EXE_PIPE_READ, NULL);
ecore_exe_pipe_run
Ecore_Exe * ecore_exe_pipe_run(const char *exe_cmd, Ecore_Exe_Flags flags, const void *data)
Spawns a child process with its stdin/out available for communication.
Definition: ecore_exe.c:64
ECORE_EXE_PIPE_READ
@ ECORE_EXE_PIPE_READ
Exe Pipe Read mask.
Definition: ecore_exe_eo.h:50
ECORE_EXE_PIPE_WRITE
@ ECORE_EXE_PIPE_WRITE
Exe Pipe Write mask.
Definition: ecore_exe_eo.h:51
ECORE_EXE_PIPE_READ_LINE_BUFFERED
@ ECORE_EXE_PIPE_READ_LINE_BUFFERED
Reads are buffered until a newline and split 1 line per Ecore_Exe_Event_Data_Line.
Definition: ecore_exe_eo.h:53

With the command above we are creating our child process, the first parameter is the command to be executed, the second are the pipe flags and in our case we will write and read in the pipe so we must say what we are doing in the pipe. You may notice the flag ECORE_EXE_PIPE_READ_LINE_BUFFERED, this means that reads are buffered until I find a newline. And the third parameter is data that we would like to send to the process in its creating. This case we are sending nothing, so just use NULL.

Then we check if the process was created:

if (childHandle == NULL)
{
fprintf(stderr, "Could not create a child process!\n");
goto ecore_shutdown;
}

After this we get the PID of the child process and just print it in the screen. The PID stands for Process identification. This is just an internal identifier of your process:

childPid = ecore_exe_pid_get(childHandle);
if (childPid == -1)
fprintf(stderr, "Could not retrieve the PID!\n");
ecore_exe_pid_get
pid_t ecore_exe_pid_get(const Ecore_Exe *exe)
Retrieves the process ID of the given spawned process.
Definition: ecore_exe.c:215

The way that Ecore_exe works is: when we want to read data sent from our child we must use an ecore event. So let's start register our event listener:

Now to send messages to our child we will use a timer, so every 1 second we will send a message to the child.

After all this we start the main loop. Now let's pass to the callback functions.

Now we will see how we actually send the data and receive it. Let's start with _sendMessage:

_sendMessage(void *data)
{
static int numberOfMessages = 0;
Ecore_Exe *childHandle = (Ecore_Exe *)data;
char msg[BUFFER_SIZE];
sprintf(msg, " Message: %d\n", numberOfMessages);
numberOfMessages++;
if (ecore_exe_send(childHandle, msg, strlen(msg)) != EINA_TRUE)
fprintf(stderr, "Could not send my name to the child\n");
else
printf(
"I'm the father and I sent this message to the child: %s\n", msg);
return ECORE_CALLBACK_RENEW;
}
ecore_exe_send
Eina_Bool ecore_exe_send(Ecore_Exe *exe, const void *data, int size)
Sends data to the given child process which it receives on stdin.
Definition: ecore_exe.c:115
EINA_TRUE
#define EINA_TRUE
boolean value TRUE (numerical value 1)
Definition: eina_types.h:539
Ecore_Exe
Opaque handle to manage Ecore Exe objects.

We use ecore_exe_send to send data to the child process, it's pretty simple. To know what the parameters stands for, check the docs.

Note
  The function ecore_exe_send will never block your program, also there is no partial send of the data. This means either the function will send all the data or it will fail.

Now let's take a look in our event callback and see how we retrieve the messages.

static Eina_Bool
_msg_from_child_handler(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
Ecore_Exe_Event_Data *dataFromProcess = (Ecore_Exe_Event_Data *)event;
char msg[BUFFER_SIZE];
if (dataFromProcess->size >= (BUFFER_SIZE - 1))
{
printf("Data too big for bugger. error\n");
return ECORE_CALLBACK_DONE;
}
_Ecore_Exe_Event_Data
Ecore exe event data structure.
Definition: ecore_exe_eo.h:33
_Ecore_Exe_Event_Data::size
int size
The size of this data in bytes.
Definition: ecore_exe_eo.h:37
strncpy(msg, dataFromProcess->data, dataFromProcess->size);
msg[dataFromProcess->size] = 0;
if (strcmp(msg, "quit") == 0)
{
printf("My child said to me, QUIT!\n");
ecore_main_loop_quit();
}
_Ecore_Exe_Event_Data::data
void * data
The raw binary data from the child process received.
Definition: ecore_exe_eo.h:36

It's just like an normal event, we get a reference to Ecore_Exe_Event_Data, extract the data and then show it in the screen.

And that's it, after all it's not complicated to create a process and communicate with it.