0

I have the following c++ code which writes "Line from #" to a file while managing a file lock. I am running this code on two different computers, which share at least some of their memory. That is I can access my files by logging onto either of these computers.

On the first computer I run the program as ./test 1 (e.g. so it will print Line from 1 20,000 times) and on the second computer I run the program as ./test 17. I am starting these programs close enough in time so that the writes to file.txt should be interleaved and controlled by the file locks.

The problem is that I am losing output as the file has 22,770 newlines, but it should have exactly 40,000 newlines.

wc file.txt
22770  68310 276008 file.txt

Also,

cat -n file.txt | grep 18667
18667   ne from 17

My question is why are my file locks not preventing file overwriting, and how can I fix my code so that multiple processes can write to the same file without file loss.

#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include <iostream>
using namespace std;

void inline Set_Lck(struct flock &flck, const int fd)
{
  flck.l_type = F_WRLCK;
  if (fcntl(fd, F_SETLKW, &flck) == -1) {
    perror("fcntl");
    exit(1);
  }
}

void inline Release_Lck(struct flock &flck, const int fd)
{
  flck.l_type = F_UNLCK;
  if (fcntl(fd,F_SETLK,&flck) == -1) {
    perror("fcntl");
    exit(1);
  }
}

void Print_Spec(fstream &fout, ostringstream &oss,struct flock &flck, const int fd)
{
  Set_Lck(flck,fd);
  fout.seekp(0,ios_base::end);
  fout << oss.str() << endl;
  flush(fout);
  Release_Lck(flck,fd);
}

int main(int argc, char **argv)
{
  int fd_cd;
  struct flock flock_cd;
  ostringstream oss;
  fstream comp_data;
  const string s_cd_lck = "file_lock.txt";
  const string s_cd = "file.txt";
  int my_id;

  if (argc == 1) {
    my_id = 0;
  } else if (argc == 2) {
    my_id = atoi(argv[1]);
  } else {
    fprintf(stderr,"error -- usage ./test [my_id]\n");
    exit(1);
  }

  /* Open file computed_data.txt for writing; create it if non-existent.*/
  comp_data.open(s_cd.c_str(),ios::app|ios::out);
  if (comp_data.fail()) {
    perror("comp_data.open");
    exit(1);
  }

  /* Open file that we will be locking. */
  fd_cd = open(s_cd_lck.c_str(),O_CREAT|O_WRONLY,0777);

  if (fd_cd == -1) {
    perror("fd_cd = open");
    exit(1);
  }

  /* Set up the lock. */
  flock_cd.l_type = F_WRLCK;
  flock_cd.l_whence = SEEK_SET;
  flock_cd.l_start = 0;
  flock_cd.l_len = 0;
  flock_cd.l_pid = getpid();

  for (int i = 0; i < 20000; ++i) {
    oss.str(""); /* Yes, this can be moved outside the loop. */
    oss << "Line from " << my_id << endl;
    Print_Spec(comp_data,oss,flock_cd,fd_cd);
  }

  return 0;
}

I am using c++ and this program is running on Red Hat Enterprise Linux Server release 7.2 (Maipo).

My Research
I am not sure if part of the answer comes from the following Stackoverflow post (https://stackoverflow.com/a/2059059/6417898) where they state that "locks are bound to processes."

At this website (http://perl.plover.com/yak/flock/samples/slide005.html), the author dissuades against using LOCK_UN with flock and suggests closing the file each time and reopening it as needed, so as to flush the file buffer. I don't know if this carries over with fcntl or if this is even necessary if flush the file buffer manually.

Community
  • 1
  • 1
Aguila
  • 101
  • 1
    where does this `file.txt` reside? On NFS? Fcntl is not guaranteed to work over NFS. – SergeyA Jun 03 '16 at 18:48
  • How large is `file_lock.txt`? Your `struct flock` values say to lock all the bytes in the file. What happens if the file is zero bytes? – Andrew Henle Jun 03 '16 at 21:40
  • @SergeyA `file.txt` is on NFS - thanks for letting me know about that limitation. – Aguila Jun 04 '16 at 04:32
  • @AndrewHenle file_lock.txt is 0 bytes. I added some bytes to it and re-ran it and it was still not working. Thank you for that suggestion. – Aguila Jun 04 '16 at 04:35

0 Answers0