Thread: Serialization of forked processes - best practices?

  1. #1
    Registered User
    Join Date
    Apr 2019
    Posts
    62

    Serialization of forked processes - best practices?

    Hi,

    I have a problem with a race condition involving my display and forked processes. I need to find a way to serialize writing to the display.

    The only way I know to make sure only one process can access a resource is by using lockfiles. I tried to use a variable, but that idea suffered from a race condition as well.

    Is this the best way, or am I missing something obvious?

    Ty.

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,084
    I had the impression that you tried a mutex in a recent post. Did that fail to work?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    Registered User
    Join Date
    Apr 2019
    Posts
    62
    Quote Originally Posted by laserlight View Post
    I had the impression that you tried a mutex in a recent post. Did that fail to work?
    I just learned of the word 'mutex', but haven't tried anything involving it as it appears to use 'pthreads'. With all the other new programming code I have learned recently, including 'inotify', I'm shying away from 'pthreads' for the sake of my head. If it is a better way, I can look into it, but with problems I hear people have with 'pthreads' makes me shy away from it.

    I did try using the ANSI Escape Sequences:
    Code:
    \033[s   Save cursor position and attributes
    
    \033[u   Restore cursor position and attributes
    But it didn't solve my problem.
    Last edited by Yonut; 05-15-2020 at 04:00 PM. Reason: fixed/corrected code.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,297
    > I have a problem with a race condition involving my display and forked processes.
    I think you've dug an impossibly deep hole for yourself.

    TBH, you need to rethink your approach, not try and fix the problems you have.

    Mostly, child processes would be feeding information back to the parent via pipes, so that the display is only ever updated by a single process in a controlled manner.

    But if you insist, you'll need sem_overview(7) - Linux manual page

    Why do you have multiple processes to begin with?

    If you need them, why aren't you using threads instead?
    Mutexes in threads are a lot more efficient than semaphores between processes.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Apr 2019
    Posts
    62
    Quote Originally Posted by Salem View Post
    > I have a problem with a race condition involving my display and forked processes.
    I think you've dug an impossibly deep hole for yourself.

    TBH, you need to rethink your approach, not try and fix the problems you have.
    Ha, this IS the rewrite.

    Quote Originally Posted by Salem View Post
    Mostly, child processes would be feeding information back to the parent via pipes, so that the display is only ever updated by a single process in a controlled manner.
    This sounds like a very good idea. I'll have to let my brain chew on that idea for a bit.

    Quote Originally Posted by Salem View Post
    But if you insist, you'll need sem_overview(7) - Linux manual page
    I like that this process blocks. Much more efficient than the while loop I have below.

    Quote Originally Posted by Salem View Post
    Why do you have multiple processes to begin with?

    If you need them, why aren't you using threads instead?
    Mutexes in threads are a lot more efficient than semaphores between processes.
    I have a display that is controlled by remote control. Among one of the things I was thinking of adding was a realtime clock display. I had the idea of putting the updated time in a variable and displaying the variable every time the screen changed (for user input), but if the user didn't input for anything for 10 mins then the clock would be showing 10 mins behind. And updating the whole screen without a change actually showed a very faint flicker. So the idea was to fork a process to display the time in the lower section of the screen. Then it would be it's own entity. The program displays data in the DATA_AREA which ends one line above the CLOCK_AREA. The problem comes (very occasionally) when the DATA_AREA is updating and the clock forked process updates the CLOCK_AREA. Some of the DATA_AREA would start writing from the end of the CLOCK_AREA. Even with the save/restore cursor position, somehow the clock fork was able to interrupt the parent displaying the DATA_AREA. That's the gist.

    Last night I came up with a solution. Not the best, but it works. I know the while loop is a waste of processes, but the file lock doesn't ever last long enough to matter. This is what I came up with:
    Code:
    void get_display_lock (void)
    {
    // Declare variables.
        int fd = {0};
    
    // Loop until file is able to be created.
        while((fd = open(LOCK_FILE, O_CREAT|O_EXCL|O_WRONLY)) == -1)
            ;
    
    // Close the file.
        close(fd);
    }
    
    void remove_display_lock (void)
    {
    // Remove the lock file.
        if(unlink(LOCK_FILE) == -1)
        {
            fprintf(stderr, "%s: remove_display_lock error: unlink failed (%s)\n", program_name, strerror(errno));
            exit(EXIT_FAILURE);
        }
    }
    Last edited by Yonut; 05-16-2020 at 10:54 AM. Reason: Fixed code.

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,297
    > Among one of the things I was thinking of adding was a realtime clock display. I had the idea of putting
    > the updated time in a variable and displaying the variable every time the screen changed (for user input),
    > but if the user didn't input for anything for 10 mins then the clock would be showing 10 mins behind.
    The way I would do this would be to put a timeout on the user input.

    Code:
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/select.h>
    
    int main() {
      int ch;
    
      printf("Type stuff ");
      fflush(stdout);
      while ( ch != 'q' ) {
        fd_set read_set;
        FD_ZERO(&read_set);
        FD_SET(0,&read_set);
        struct timeval tmo = { 1, 0 }; // 1 second
        if ( select(1,&read_set,NULL,NULL,&tmo) == 1 ) {
          ch = getchar();
          printf("%c",ch);
          fflush(stdout);
        } else {
          printf("?");
          fflush(stdout);
        }
      }
    }
    No messy processes or threads.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  7. #7
    Registered User
    Join Date
    Apr 2019
    Posts
    62
    Quote Originally Posted by Salem View Post
    The way I would do this would be to put a timeout on the user input.
    ...
    No messy processes or threads.
    That is very cool, I think I might be able to use that in the future. But part of the appeal of a forked function is being able to send 'SIGSTOP' and 'SIGCONT' to pause and resume the forked process for when there is video playing on the display.

    And Salem, I would like to point out something I find ironic, and I want you to know that I respect you deeply...

    I love your animated avatar. It speaks volumes. Don't use `void main()`, have your program return some code for the operating system to handle. Use 'int main()'. And I totally agree. But you seem to be missing the one statement that is needed to fully accomplish this task.

    I'm sure it was just a typo though.

  8. #8
    Registered User
    Join Date
    Apr 2019
    Posts
    62

    Understanding select().

    Hi,

    In a earlier thread, Salem mentioned the use of `select()` and gave the following code example.
    Quote Originally Posted by Salem View Post
    Code:
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/select.h>
    
    int main() {
      int ch;
    
      printf("Type stuff ");
      fflush(stdout);
      while ( ch != 'q' ) {
        fd_set read_set;
        FD_ZERO(&read_set);
        FD_SET(0,&read_set);
        struct timeval tmo = { 1, 0 }; // 1 second
        if ( select(1,&read_set,NULL,NULL,&tmo) == 1 ) {
          ch = getchar();
          printf("%c",ch);
          fflush(stdout);
        } else {
          printf("?");
          fflush(stdout);
        }
      }
    }
    I'm trying to understand how this function operates. I noticed that the loop test is never false. So I changed the code to this to help understand what's happening:
    Code:
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/select.h>
    
    int main (void)
    {
    // Declare variables.
        int ch = 'd';
    
        printf("Type stuff ");
        fflush(stdout);
    
        while(ch != 'q')
        {
            fd_set read_set;
            FD_ZERO(&read_set);
            FD_SET(0,&read_set);
            struct timeval tmo = { 1, 0 }; // 1 second
    
            if(select(1, &read_set, NULL, NULL, &tmo) == 1)
            {
                ch = getchar();
                printf("%c",ch);
                fflush(stdout);
            }
            else
            {
                printf("?%c", ch);
                fflush(stdout);
            }
        }
    
        return(0);
    }
    As you can see, the value of `ch` only changes in the first part of the `if` statement, and never changes from the initialization value it started with, in the `else`. How can this be? And how would you stop this loop? Do I need to get rid of the loop test and just make it `while(1)`? Then do the character test of `q` in the first part of the `if`? And why doesn't the value of `ch` change for the `else`? It was declared at the start of the function, so in my mind, it shouldn't be a scope issue.

    Ty.

  9. #9
    Registered User
    Join Date
    Apr 2019
    Posts
    62
    Ok, I just changed the code to see if I could break the loop, and nothing breaks the loop.
    Code:
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/select.h>
    
    int main (void)
    {
    // Declare variables.
        char ch = 'd';
    
    // Loop forever.
        while(1)
        {
            fd_set read_set;
            FD_ZERO(&read_set);
            FD_SET(0, &read_set);
            struct timeval tmo = {1, 0}; // 1 second
    
            if(select(1, &read_set, NULL, NULL, &tmo) == 1)
            {
                return(0);
            }
            else
            {
                printf("?%c", ch);
                fflush(stdout);
            }
        }
    
        return(0);
    This is very confusing to me. Return only works in the `else` not the `if`. How?
    Last edited by Yonut; 06-04-2020 at 02:18 PM.

  10. #10
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    747
    There is a bad interaction happening between stdio and the low-level select file operation. getchar() (and stdio in general) works by reading a chunk of data at a time into an internal buffer and returning only what the program asks for. For example, when a program does a getchar(), if the internal buffer is empty, stdio first reads a chunk of data through the read system call; getchar() then returns the next available char from the internal buffer.

    The problem is that when you do a select, it knows nothing about stdio's internal buffer, so it reports a timeout, even if there's more data in stdio's internal buffer.

    Instead of using getchar(), you can call read directly.

    Note that you have to be careful when mixing stdio and the underlying file system calls.

  11. #11
    Registered User
    Join Date
    Apr 2019
    Posts
    62
    Quote Originally Posted by christop View Post
    Instead of using getchar(), you can call read directly.
    Sorry, I'm not quite understanding. The second example I gave doesn't have `getchar()` at all. And it will never enter the first part of the `if`. Also, why would what you said affect the `ch` variable that was declared at the start of the function?

  12. #12
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,297
    Maybe this will help, since it fixes the buffering of stdin which makes it kinda hard to see what's going on.
    Code:
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/select.h>
    #include <termios.h>
    
    int main() {
      int ch;
      struct termios oldt, newt;
    
      tcgetattr ( STDIN_FILENO, &oldt );
      newt = oldt;
      newt.c_lflag &= ~( ICANON | ECHO );
      newt.c_cc[VMIN] = 0;
      newt.c_cc[VTIME] = 0;
      tcsetattr ( STDIN_FILENO, TCSANOW, &newt );
    
      printf("Type stuff ");
      fflush(stdout);
    
      while ( ch != 'q' ) {
        fd_set read_set;
        FD_ZERO(&read_set);
        FD_SET(0,&read_set);
        struct timeval tmo = { 1, 0 }; // 1 second
        if ( select(1,&read_set,NULL,NULL,&tmo) == 1 ) {
          ch = getchar();
          printf("Char=%c\n",ch);
          fflush(stdout);
        } else {
          printf("timeout\n");
          fflush(stdout);
        }
      }
    
      tcsetattr ( STDIN_FILENO, TCSANOW, &oldt );
    
      return 0;
    }
    
    
    $ ./a.out 
    Type stuff timeout
    Char=h
    Char=e
    Char=l
    Char=l
    Char=o
    Char=
    
    timeout
    Char=q
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  13. #13
    Registered User
    Join Date
    Apr 2019
    Posts
    62
    Sweet, and does using this change any forked processes inputs to character mode as well?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. gtk in forked process
    By abc_killer in forum C Programming
    Replies: 1
    Last Post: 12-13-2013, 12:05 PM
  2. C forked processes
    By nuubik in forum C Programming
    Replies: 9
    Last Post: 10-29-2010, 04:11 PM
  3. accessing shared memory via forked processes
    By rklockow in forum C Programming
    Replies: 7
    Last Post: 06-30-2010, 05:44 PM
  4. Replies: 3
    Last Post: 06-12-2007, 11:21 AM
  5. writing to file with multiple forked process
    By hiawatha in forum C Programming
    Replies: 7
    Last Post: 04-18-2007, 06:30 PM

Tags for this Thread