Hello,
I need to implement a locking system in C. My problem is how to make the check if the lock file exist and locking it atomic operation.
I want to make something like this:
FILE* lock_fname;
lock_fname = fopen ( "file.lock", "r");
/*check if file exsists*/
if (lock_fname)
{
fclose (lock_fname);
lock_fname = fopen ( "file.lock", "w");
/* file is now locked */
}
else
{
/* looks like file is locked */
}
But this is not very good solution. How can make the check and the creation of lock file atomic operation?
Also there is a good chance that my code may have to run on Windows so I have to stick to standard C functions.
Have you got any suggestions?
Thank you!
EDIT:
This is a good solution, but it won't compile on windows I think:
#include <fcntl.h> // for open()
#include <cerrno> // for errno
#include <cstdio> // for perror()
int fd;
fd=open("password.lck", O_WRONLY | O_CREAT | O_EXCL)
sig_atomic_t is a datatype which guarantees atomic operations when basic arithmetic operations, like + -, are involved. This is because the operation takes only one uninterruptible cpu instruction.
That's pretty much all there is for atomic operations defined by the C standard.
fopen fclose, etc, are all part of the C library and are hundreds of cpu instructions long, as well as requiring disk i/o. None of these functions could ever be atomic. They are never guaranteed to be atomic. In UNIX, none of the file I/O system calls like read, write are atomic either. The C library sits on top of these system calls.
How about opening a TCP socket at a specific port on 127.0.0.1? UNIX won't let more than one program bind the same port, and though Windows sometimes will, I think it can be configured not to.
There are lots of file operation system calls with absolute guarantees of atomicity. The link system call is the one most often used in locking techniques. First create a temporary file in a well known directory. Then attempt to link() it to a well known name. If two processes try this at the same time, one will succeed and the other fail. If the link works, you own the lock.
For file updates, you can link the file to a lock file. Programs that update /etc/passwd and /etc/shadow often use this technique. They try to link /etc/passwd to /etc/opasswd and /etc/shadow to /etc/oshadow. Using truss and strace, I see that redhat linux and Solaris 8 are still doing that. This was the original way to lock files between cooperating processes.
I think this could work. I extracted the comment from open function man page.
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd;
/* O_EXCL:
If O_CREAT and O_EXCL are set, open() shall fail if the file
exists. The check for the existence of the file and the cre-
ation of the file if it does not exist shall be atomic with
respect to other threads executing open() naming the same file-
name in the same directory with O_EXCL and O_CREAT set. If
O_EXCL and O_CREAT are set, and path names a symbolic link,
open() shall fail and set errno to [EEXIST], regardless of the
contents of the symbolic link. If O_EXCL is set and O_CREAT is
not set, the result is undefined.
*/
if ( (fd = open("./test.txt", O_RDWR|O_CREAT|O_EXCL)) < 0 )
{
perror("open");
}
else
{
/*here you can use fcntl in order to lock the file*/
}
return 0;
}
lagigliaivan, this works, but it won't compile on windows I think. Actually I've solved my problem with windows specific functions
Many thanks to everyone about the replies!