I am trying to understand flock(2)
in <sys/file.h>
I'd like to quote the part that I did not understand, from the online Linux manpage:
LOCK_EX Place an exclusive lock. Only one process may hold an exclusive lock for a given file at a given time.
This is exclusively about LOCK_EX
. The code will be shown below but, in short, the parent process opens a file, and fork the child process. The parent process sleeps 1s first so the child process can get the flock first. The child process acquires flock, and then sleeps 5s. In the meantime, the parent process wakes up, uses the same file descriptor to read/write the file.
My guess was that the parent process would be somehow denied -- e.g. read/write
returns -1. However, it looks like the parent process had no problem to write/read the file.
What exactly does flock
"lock" then?
Here's the code:
#include <sys/file.h>
#include <sys/wait.h>
#include <unistd.h>
#include <chrono>
#include <cstring>
#include <iostream>
#include <thread>
static constexpr char lock_file[] = "/tmp/test-flock.lock";
int main() {
using namespace std::chrono_literals;
char buf[201] = {0};
if (readlink("/proc/self/exe", buf, 200) == -1) {
std::cerr << "Readlink failed" << std::endl << std::flush;
return 2;
}
auto fd = open(lock_file, O_RDWR);
pid_t pid = fork();
if (pid == 0) {
std::cout << "Child process #" << getpid() << " tries to acquire the lock..."
<< std::endl << std::flush;
auto ret_code = flock(fd, LOCK_EX);
std::cout << "Child process #" << getpid() << " got flock and will sleep "
<< std::endl << std::flush;
std::this_thread::sleep_for(5s);
char msg[] = "Hello";
lseek(fd, 0, SEEK_SET);
write(fd, msg, 5);
std::cout << "Child process #" << getpid() << " wrote " << msg
<< std::endl << std::flush;
std::cout << "Child process #" << getpid() << " released lock with " << ret_code
<< std::endl << std::flush;
return 0;
}
int status;
char parent_msg[6] = {0};
std::cout << "Parent #" << getpid() << " sleeps so the child can take flock "
<< std::endl << std::flush;
std::this_thread::sleep_for(1s);
do {
/*
lseek(fd, 0, SEEK_SET);
auto wr_ret_code = write(fd, "HEHEH", 5);
std::cout << "Parent #" << getpid() << " returned from write with "
<< wr_ret_code << std::endl << std::flush;
*/
lseek(fd, 0, SEEK_SET);
auto rd_ret_code = read(fd, parent_msg, 5);
std::cout << "Parent #" << getpid() << " returned from read with "
<< rd_ret_code << std::endl << std::flush;
std::cout << "Parent #" << getpid() << " read " << parent_msg
<< std::endl << std::flush;
if (std::string(parent_msg) == "Hello") {
break;
}
memset(parent_msg, 0, 6);
std::this_thread::sleep_for(2s);
} while (true);
waitpid(pid, &status, 0);
std::cout << "Parent #" << getpid() << " is done with waiting " << pid
<< std::endl << std::flush;
std::cout << "And, parent read " << parent_msg << std::endl << std::flush;
lseek(fd, 0, SEEK_SET);
write(fd, "HEHEA", 5);
return 0;
}
Here's how I tested:
$ echo "HEHEA" > /tmp/test-flock.lock
$ ./a.out # write part being commented out
Parent #16712 sleeps so the child can take flock
Child process #16713 tries to acquire the lock...
Child process #16713 got flock and will sleep
Parent #16712 returned from read with 5
Parent #16712 read HEHEH
Parent #16712 returned from read with 5
Parent #16712 read HEHEH
Child process #16713 wrote Hello
Child process #16713 released lock with 0
Parent #16712 returned from read with 5
Parent #16712 read Hello
Parent #16712 is done with waiting 16713
And, parent read Hello
$ ./a.out # uncommented the write part.
Parent #16726 sleeps so the child can take flock
Child process #16727 tries to acquire the lock...
Child process #16727 got flock and will sleep
Parent #16726 returned from write with 5
Parent #16726 returned from read with 5
Parent #16726 read HEHEH
Parent #16726 returned from write with 5
Parent #16726 returned from read with 5
Parent #16726 read HEHEH
Child process #16727 wrote Hello
Child process #16727 released lock with 0
Parent #16726 returned from write with 5
Parent #16726 returned from read with 5
Parent #16726 read HEHEH
Parent #16726 returned from write with 5
Parent #16726 returned from read with 5
Parent #16726 read HEHEH
C-c C-c