Fix long standing race condition associated with how lockf uses open(2)

for mutual exclusion:

A brief description of the problem:

	1) Proc A picks up non-blocking lock on file X
	2) Proc B attempts to pickup lock, fails then waits
	3) Proc C attempts to pickup lock, fails then waits
	4) Proc A releases lock
	5) Proc B acquires lock, release it to pickup a non-blocking version
	6) Proc C acquires lock, release it to pickup a non-blocking version
	7) Both process B and C race each other to pickup lock again

This occurs mainly because the processes do not keep the lock after they have
been waiting on it. They drop it, attempt to re-acquire it. (They use the wait
to notify when the lock has become available then race to pick it up). This
results in additional CPU utilization during the race, and can also result
in processes picking locks up out of order.

This change attempts to correct this problem by eliminating the test/acquire
race and having the operating system handle it.

Reported by:	kris
Tested by:	kris
MFC after:	1 week
This commit is contained in:
Christian S.J. Peron 2005-10-05 17:39:15 +00:00
parent b64944a543
commit 031469eb27
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=150977

View File

@ -38,15 +38,14 @@ __FBSDID("$FreeBSD$");
#include <sysexits.h>
#include <unistd.h>
static int acquire_lock(const char *name);
static void cleanup(void);
static void killed(int sig);
static void timeout(int sig);
static void usage(void);
static void wait_for_lock(const char *name);
static int wait_for_lock(const char *name);
static const char *lockname;
static int lockfd;
static int lockfd = -1;
static int keep;
static volatile sig_atomic_t timed_out;
@ -106,11 +105,8 @@ main(int argc, char **argv)
alarm(waitsec);
}
lockfd = acquire_lock(lockname);
while (lockfd == -1 && !timed_out && waitsec != 0) {
wait_for_lock(lockname);
lockfd = acquire_lock(lockname);
}
while (lockfd == -1 && !timed_out && waitsec != 0)
lockfd = wait_for_lock(lockname);
if (waitsec > 0)
alarm(0);
@ -148,23 +144,6 @@ main(int argc, char **argv)
return WIFEXITED(status) ? WEXITSTATUS(status) : 1;
}
/*
* Try to acquire a lock on the given file, but don't wait for it. Returns
* an open file descriptor on success, or -1 on failure.
*/
static int
acquire_lock(const char *name)
{
int fd;
if ((fd = open(name, O_RDONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 0666)) == -1) {
if (errno == EAGAIN || errno == EINTR)
return -1;
err(EX_CANTCREAT, "cannot open %s", name);
}
return fd;
}
/*
* Remove the lock file.
*/
@ -210,16 +189,15 @@ usage(void)
/*
* Wait until it might be possible to acquire a lock on the given file.
*/
static void
static int
wait_for_lock(const char *name)
{
int fd;
if ((fd = open(name, O_RDONLY|O_EXLOCK)) == -1) {
if ((fd = open(name, O_CREAT|O_RDONLY|O_EXLOCK, 0666)) == -1) {
if (errno == ENOENT || errno == EINTR)
return;
return (-1);
err(EX_CANTCREAT, "cannot open %s", name);
}
close(fd);
return;
return (fd);
}