Tizen Native API  5.0
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:

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);

If you don't remenber 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:

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, Ecore_Fd_Handler *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)
     {
        fprintf(stdout, "My father sent this message to me:%s\n", message);
        fflush(stdout);
        return ECORE_CALLBACK_RENEW;
     }
   else
     {
        fprintf(stdout, "quit\n");
        fflush(stdout);
        ecore_main_loop_quit();
        return ECORE_CALLBACK_DONE;
     }
}

int
main(void)
{
   if (!ecore_init())
     goto error;

   ecore_main_fd_handler_add(STDIN_FILENO,
                             ECORE_FD_READ,
                             _fd_handler_cb,
                             NULL, NULL, NULL);
   ecore_main_loop_begin();

   ecore_shutdown();

   return EXIT_SUCCESS;

error:
   return EXIT_FAILURE;
}

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);

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 retrive the PID!\n");
   else
     fprintf(stdout, "The child process has PID:%u\n", (unsigned int)childPid);

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:

   ecore_event_handler_add(ECORE_EXE_EVENT_DATA, _msg_from_child_handler, NULL);

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

   ecore_timer_add(1, _sendMessage, childHandle);

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
     fprintf(stdout,
             "I'm the father and I sent this message to the child: %s\n", msg);

   return ECORE_CALLBACK_RENEW;
}

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))
     {
        fprintf(stdout, "Data too big for bugger. error\n");
        return ECORE_CALLBACK_DONE;
     }

   strncpy(msg, dataFromProcess->data, dataFromProcess->size);
   msg[dataFromProcess->size] = 0;

   if (strcmp(msg, "quit") == 0)
     {
        fprintf(stdout, "My child said to me, QUIT!\n");
        ecore_main_loop_quit();
     }

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.