0

I'm writing unit tests for a function that may lock a file (using fcntl(fd, F_SETLK, ...)) under some conditions.

I want my unit test to be able to EXPECT that the file is or is not locked at certain points. But I can't find any way to test that. I've tried using F_GETLK, but it will only tell you if the lock would not be able to be placed. Since a given process can re-lock the same file as often as it wants, F_GETLK is returning F_UNLCK, indicating the file is unlocked.

For instance, if I run the following little program:

int main(int argc, char** argv) {
  int fd = open("/tmp/my_test_file", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd < 0) {
    return EXIT_FAILURE;
  }
  // Initial lock
  struct flock lock;
  lock.l_type = F_WRLCK;
  lock.l_whence = SEEK_SET;
  lock.l_start = 0;
  lock.l_len = 0;  // Lock entire file.
  if (fcntl(fd, F_SETLK, &lock) < 0) {
    return EXIT_FAILURE;
  }
  // Test lock:
  lock.l_type = F_WRLCK;
  lock.l_pid = 0;
  if (fcntl(fd, F_GETLK, &lock) < 0) {
    return EXIT_FAILURE;
  }
  switch (lock.l_type) {
    case F_WRLCK:
      std::cout << lock.l_pid << " is holding the lock\n";
      break;
    case F_UNLCK:
      std::cout << "File is unlocked\n";
      break;
    default:
      std::cout << "Unexpected " << lock.l_type << "\n";
      break;
  }
  return EXIT_SUCCESS;
}

It will print:

File is unlocked

So, is there a way for a process to test if it is holding an fcntl file lock?

Also, are there other kinds of (Linux-portable!) file locks I could be using that would solve my problem?

melpomene
  • 84,125
  • 8
  • 85
  • 148
Ian Barkley-Yeung
  • 141
  • 2
  • 2
  • 7
  • I don't think there's an API, but see the answers [here](https://unix.stackexchange.com/questions/85994/how-to-list-processes-locking-file) for some ideas. – Barmar May 02 '19 at 02:16
  • 1
    Your unit test should be done from a separate process. You want to test that the lock *actually works*, not just that some API has been called. – user207421 May 02 '19 at 02:17
  • @user207421 - Ya, I was holding to avoid spawning other processes, but it doesn't look like I can. If you rewrite your comment as an answer, I'll accept it. – Ian Barkley-Yeung May 03 '19 at 22:16

1 Answers1

0

Well I am not aware of any "already available library” for this, but on implementation level I would suggest you to have a log file which keeps track of that.

You can simply create a file named “log”, mmap(2) it as MAP_SHARED into each process accessing the file and whenever a file lock succeeds, write the current process’ pid at the end of that log file maintaining offset to the end with CAS. That will help you analyse order of locks.

Maybe simply by opening file in append more and writing to the end, the pid of current process.

Or maybe a quicker way to do so such a test is to create a fifo file by mkfifo(2) and write to that file.

Sahil
  • 59
  • 11
  • 2
    Wouldn't it be simplest to open the log file in append mode? – Barmar May 02 '19 at 02:18
  • Well tbh I am not very sure about that but most probably append places the offset at the end of the file and maybe if two processes open the file at same time they may end up writing in different orders. Fifo file is always first in first out. Still not very sure about append mode. – Sahil May 02 '19 at 02:26
  • 2
    Append atomically goes to the end of file before each write. – Barmar May 02 '19 at 02:27
  • If you use a mapped file you'll need to use locking to coordinate access to it. You also can't grow a file when writing to mapped segment. – Barmar May 02 '19 at 02:28
  • Thanks for updating me with the info! I will update my answer – Sahil May 02 '19 at 02:29
  • 1
    I just worry that this lock tracing code could require more testing than the locking code that it's intended to test. – Barmar May 02 '19 at 02:31
  • 1
    And it doesn't really test the actual lock, only the sequence of system calls. Tests should test behaviour, not implementation. For example if you switched to using `flock()` you should not have to rewrite the tests. @Barmar – user207421 May 02 '19 at 02:35
  • @user207421 It's a workaround, since there's no easy way to test this behavior. – Barmar May 02 '19 at 02:36
  • It's basically an invalid test, and the easy way is to use a separate process. @Barmar – user207421 May 02 '19 at 02:37