13.18 Open File Description Locks Example

Here is an example of using open file description locks in a threaded program. If this program used process-associated locks, then it would be subject to data corruption because process-associated locks are shared by the threads inside a process, and thus cannot be used by one thread to lock out another thread in the same process.

Proper error handling has been omitted in the following program for brevity.


#define _GNU_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>

#define FILENAME        "/tmp/foo"
#define NUM_THREADS     3
#define ITERATIONS      5

void *
thread_start (void *arg)
{
  int i, fd, len;
  long tid = (long) arg;
  char buf[256];
  struct flock lck = {
    .l_whence = SEEK_SET,
    .l_start = 0,
    .l_len = 1,
  };

  fd = open ("/tmp/foo", O_RDWR | O_CREAT, 0666);

  for (i = 0; i < ITERATIONS; i++)
    {
      lck.l_type = F_WRLCK;
      fcntl (fd, F_OFD_SETLKW, &lck);

      len = sprintf (buf, "%d: tid=%ld fd=%d\n", i, tid, fd);

      lseek (fd, 0, SEEK_END);
      write (fd, buf, len);
      fsync (fd);

      lck.l_type = F_UNLCK;
      fcntl (fd, F_OFD_SETLK, &lck);

      /* sleep to ensure lock is yielded to another thread */
      usleep (1);
    }
  pthread_exit (NULL);
}

int
main (int argc, char **argv)
{
  long i;
  pthread_t threads[NUM_THREADS];

  truncate (FILENAME, 0);

  for (i = 0; i < NUM_THREADS; i++)
    pthread_create (&threads[i], NULL, thread_start, (void *) i);

  pthread_exit (NULL);
  return 0;
}

This example creates three threads each of which loops five times, appending to the file. Access to the file is serialized via open file description locks. If we compile and run the above program, we’ll end up with /tmp/foo that has 15 lines in it.

If we, however, were to replace the F_OFD_SETLK and F_OFD_SETLKW commands with their process-associated lock equivalents, the locking essentially becomes a noop since it is all done within the context of the same process. That leads to data corruption (typically manifested as missing lines) as some threads race in and overwrite the data written by others.