Search code examples
cflock

Write() works even if i blocked my file by other process


When I run my program:

int i = fork();

if (!i){
    int id = open("shared.txt", 0600 | O_WRONLY);
    if(flock(id, LOCK_EX | LOCK_NB) == -1)
        perror("flock_ch");
if(write(id, "child", 5)) printf("child did it\n");
else perror("write_ch");
sleep(3);
    close(id);
}
else {
    int id = open("shared.txt", 0600 | O_WRONLY);
    if(flock(id, LOCK_EX | LOCK_NB) == -1)
       perror("flock_PR");
if(write(id, "parent", 6)) printf("parent did it\n");
else perror("write_pr");
sleep(3);
    close(id);
}

wait(NULL);

I expect that only one process will write something in the file, but even if the file is locked, two of processes write in the file.


Solution

  • Both processes are writing to the file because you're ignoring the error return value from flock(). When I run your code, I see:

    parent did it
    flock_ch: Resource temporarily unavailable
    child did it
    

    That flock_ch: Resource temporarily unavailable is because flock() is returning with an error and telling you the file is locked. You're printing out an error message, but not otherwise responding to the error message. You probably want to either (a) exit or (b) loop until flock() succeeds.

    Using a loop might look like:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/file.h>
    #include <sys/wait.h>
    #include <unistd.h>
    
    #define LOCK_FLAGS (LOCK_EX | LOCK_NB)
    
    void parent() {
      int fd = open("shared.txt", 0600 | O_WRONLY);
    
      while (flock(fd, LOCK_FLAGS) == -1) {
        printf("parent waiting for lock\n");
        sleep(1);
      }
      printf("parent acquired lock\n");
    
      if (write(fd, "parent", 6) >= 0) {
        printf("parent wrote to file\n");
      } else {
        perror("parent: write failed:");
        exit(1);
      }
    
      sleep(3);
    
      close(fd);
    }
    
    void child() {
      int fd = open("shared.txt", 0600 | O_WRONLY);
    
      while (flock(fd, LOCK_FLAGS) == -1) {
        printf("child waiting for lock\n");
        sleep(1);
      }
      printf("child acquired lock\n");
    
      if (write(fd, "child", 6) >= 0) {
        printf("child wrote to file\n");
      } else {
        perror("child: write failed:");
        exit(1);
      }
    
      sleep(3);
    
      close(fd);
    }
    
    int main() {
      // ensure "shared.txt" exists
      int fd = open("shared.txt", O_CREAT | O_WRONLY, 0600);
      close(fd);
    
      pid_t pid = fork();
    
      if (pid == 0) {
        child();
        exit(0);
      }
      parent();
      wait(NULL);
    
      return 0;
    }
    

    Running the above produces:

    parent acquired lock
    parent wrote to file
    child waiting for lock
    child waiting for lock
    child waiting for lock
    child acquired lock
    child wrote to file
    

    Alternately, drop the LOCK_NB flag so that flock() blocks until the lock is available. That might look like:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/file.h>
    #include <sys/wait.h>
    #include <unistd.h>
    
    #define LOCK_FLAGS (LOCK_EX) // | LOCK_NB)
    
    void parent() {
      int fd = open("shared.txt", 0600 | O_WRONLY);
    
      if (flock(fd, LOCK_FLAGS) == -1) {
          perror("parent: flock:");
          exit(1);
      }
    
      printf("parent acquired lock\n");
    
      if (write(fd, "parent", 6) >= 0) {
        printf("parent wrote to file\n");
      } else {
        perror("parent: write failed:");
        exit(1);
      }
    
      sleep(3);
    
      close(fd);
    }
    
    void child() {
      int fd = open("shared.txt", 0600 | O_WRONLY);
    
      if (flock(fd, LOCK_FLAGS) == -1) {
          perror("child: flock:");
          exit(1);
      }
    
      printf("child acquired lock\n");
    
      if (write(fd, "child", 6) >= 0) {
        printf("child wrote to file\n");
      } else {
        perror("child: write failed:");
        exit(1);
      }
    
      sleep(3);
    
      close(fd);
    }
    
    int main() {
      // ensure "shared.txt" exists
      int fd = open("shared.txt", O_CREAT | O_WRONLY, 0600);
      close(fd);
    
      pid_t pid = fork();
    
      if (pid == 0) {
        child();
        exit(0);
      }
      parent();
      wait(NULL);
    
      return 0;
    }
    

    And running the above produces:

    parent acquired lock
    parent wrote to file
    child acquired lock
    child wrote to file