Search code examples
cmp3raspberry-pincurses

Best practice for running two concurrent processes in C (on a Raspberry Pi)


I'm programming a "simple" jukebox in C which is to run on a Raspberry Pi and I'm having difficulties in figuring out what's the best way to optimize its performance.

The way it is set up is that I have one process which handles all the graphics (via ncurses) and data handling, and another which i send commands to an mp3-playing application(xmms2) with and then I connect the two with two pipes to form a simple bridge which sends messages like "get current playlist position" and responds with "current playlist position: 0" (although not as verbose as that).

All pipes are set to be non-blocking, but I still experience a lot of lag when sending these commands.

Realize this may be a bit of an abstract question, but I'm not super well versed in C and would appreciate any type of input as to wether or not I've gone in entirely the wrong direction or not with how this is set up.

Thanks in advance!

main loop:

init_xmms_bridge();

pid_t pid = fork();

while(true == true)
{
    update_xmms_bridge(pid);
    if(pid != 0)
        update_screen();
}

graphics.c

void update_screen(void)
{
    /* update playlist if that has changed, 
    library display if keys has been pressed etc */

    usleep(50000); // Wait .05 seconds
    refresh(); // Refresh ncurses screen
}

xmms_bridge.c (partial)

void init_xmms_bridge(void)
{
    pipe(xmms_pipe_in);
    pipe(xmms_pipe_out);

    // Set all pipes as non-blocking
    int flags = fcntl(xmms_pipe_in[0], F_GETFL, 0);
    fcntl(xmms_pipe_in[0], F_SETFL, flags | O_NONBLOCK);

    flags = fcntl(xmms_pipe_in[1], F_GETFL, 0);
    fcntl(xmms_pipe_in[1], F_SETFL, flags | O_NONBLOCK);

    flags = fcntl(xmms_pipe_out[0], F_GETFL, 0);
    fcntl(xmms_pipe_out[0], F_SETFL, flags | O_NONBLOCK);

    flags = fcntl(xmms_pipe_out[1], F_GETFL, 0);
    fcntl(xmms_pipe_out[1], F_SETFL, flags | O_NONBLOCK);

}

// Receive data from pipes
void update_xmms_bridge(pid_t process_id)
{
    if(process_id == 0) // Child process
    {
        close(xmms_pipe_out[1]);
        bytes_read_out = read(xmms_pipe_out[0],xmms_pipe_buffer_out,sizeof(xmms_pipe_buffer_out));

        if(bytes_read_out != -1)
            xmms_receive_call(xmms_pipe_buffer_out,XMMSDirectionOut);
    }
    else
    {
        close(xmms_pipe_in[1]);
        bytes_read_in = read(xmms_pipe_in[0],xmms_pipe_buffer_in,sizeof(xmms_pipe_buffer_in));

        if(bytes_read_in != -1)
            xmms_receive_call(xmms_pipe_buffer_in,XMMSDirectionIn);
    }
}

// Send data to pipes
void bridge_call(int pipe[2],const char command,char *parameters)
{
    // Make sure it works even if parameters is null
    char call[parameters == NULL ? 3 : strlen(parameters)+3];

    /* Add separator to deal with the fact
    that multiple calls can be made before 
    the loop reads the pipe. */
    call[0] = SEPARATOR_CHARACTER;
    call[1] = command;
    call[2] = '\0';

    // Concentate string before sending through pipe
    if(parameters != NULL)
        strcat(call,parameters);

    close(pipe[0]);
    write(pipe[1],call,strlen(call));
}

void xmms_bridge_call(const char command,char *parameters)
{
    bridge_call(xmms_pipe_out,command,parameters);
}

void jukebox_bridge_call(const char command,char *parameters)
{
    bridge_call(xmms_pipe_in,command,parameters);
}

Solution

  • After reading up on what user Kenneth pointed out, I solved this by replacing the process forking with threads. Since most my calls were supposed to be one way, my typical thread code looked like this:

    pthread_t thread;
    pthread_attr_t thread_attr;
    int thread_error;
    
    thread_error = pthread_attr_init(&thread);
    
    if(thread_error)
    {
        /* Handle error */
        return;
    }
    
    /* Make thread detached so I don't need to worry about it after it's creation */
    result = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
    
    if(!thread_error)
    {
        thread_error = pthread_create(&thread,&thread_attr,<xmms-calling-funtion>,<arguments>);
    
        pthread_attr_destroy(&thread_attr);
    
        if(thread_error)
            /* Handle error */
    }
    else
        /* Handle error */
    

    As user Craig mentioned, there is a xmms2 C client library, but I never managed to link it to my program although I'm sure it would have been preferable to have used it. I still feel that this answer pertains more to my question which was more about performance on the Raspberry Pi than xmms2 specifically though.

    If anyone does build a project on a Raspberry Pi and manages to successfully link it to the xmms2 C client library, I would love to hear how you did it!

    Link to the client library tutorial repository:

    git://git.xmms2.org/xmms2/xmms2-tutorial.git
    

    For curious people, this is what the user interface ended up looking like:

    Picture of Raspberry Pi based jukebox running ncurses