Simple file locking in FreeBSD 7

I'm trying to do some simple file locking in freebsd 7 with userland apps. I'm having some problems with atomicity, though, with advisory locks (flock).

Advisory locks present a problem for me because you:

1) Check to see if there is an advisory lock on the file.
2) If no lock, then you lock the file and do your business with it.

But what if the process is put to sleep between steps 1 and 2?

Is there a userland function that will perform steps 1 and 2 atomically? My program is not using pthreads, either. This is between two processes that have forked. Any such beast available?
 
The return value from flock() tells you if you got the lock or not, so 1 and 2 coalesce into just one operation.

There's two alternatives:
- Use LOCK_NB, check the return value, and try again if you didn't get it.
- Don't use LOCK_NB, which means flock() will only return when you get the lock (or if there's an error).
 
Thanks DJN.

This is between 2 processes that have forked, and if I try to lock and unlock the file after I've forked, the locking mechanism is ignored.

I was able to get things running by taking my fork out of the code and manually starting two seperate processes. Then the flock is not ignored.

Is there a way to get flock to work between a parent and child?

Here is the simple test program I wrote which ignores flocks because it forks. The desired result is to open the testfile when it has completed and see a bunch of entries saying either "parent" or "child" (depending on which process was scheduled first), and then a bunch of entries with the opposite of the first one that printed. Instead, they're all intermixed.

Should say:

parent
parent
parent
parent
parent
parent
parent
child
child
child
child
child

Instead it is all intermingled:

parent
child
parent
child
child
parent
child
parent
child

int main()
{

int pid, file, a;
char buffer[10];

file = open("/home/jamie/testfile", O_RDWR | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR);
bzero ( buffer, 10 );
pid = fork();

if ( pid > 0 )
flock ( file, LOCK_EX);

if ( pid > 0 )
strcpy ( buffer, "parent\n");
else
strcpy ( buffer, "child\n");
for ( a=1; a < 10000; a++){
write (file, buffer, 10);
}

if ( pid > 0 )
flock ( file, LOCK_UN);


waitpid(pid);
close(file);
return 0;
}
 
Well, you only call flock in the child, and it's an advisory lock (that is: it doesn't lock the file, it's just a random lock that happens to be associated with the file), so that's to be expected.

In other words, remove the if before the flock().
Oh, and use [ code] [ /code] around code in your posts. :)
 
Thanks again for the reply. I've re-written and ran this code. Seems as though the child and parent are ignoring the flock(). One should get the lock first, blocking the other, run it's loop, and then allow the other to run:

Results:
prints 121212121212211212212121212121212112121212

In the output file. Here is the new code:

Code:
int main()
{

        int pid, file, a;
        char buffer[1];

        file = open("/home/jamie/testfile", O_RDWR | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR);
        bzero ( buffer, 1 );
        pid = fork();

        flock ( file, LOCK_EX);

        if ( pid > 0 )
        strcpy ( buffer, "1");
        else
        strcpy ( buffer, "2");


        for ( a=1; a < 100; a++){
        write (file, buffer, 1);
        sleep (1);
        }

        flock ( file, LOCK_UN);

        waitpid(pid);
        close(file);
        return 0;
}
 
From the man page:
Locks are on files, not file descriptors. That is, file descriptors
duplicated through dup(2) or fork(2) do not result in multiple instances
of a lock, but rather multiple references to a single lock. If a process
holding a lock on a file forks and the child explicitly unlocks the file,
the parent will lose its lock.

In other words you should open the file after forking.
 
He doesn't actually lock it until after forking, though, and it does say that forking will create references to the same lock, not separate locks.
 
Aha, that was it. I found that part of the manpage confusing myself, but I did try opening after the fork, and it does work. Thanks to both you guys for helping me with this.

Here is the output of this new code:

Parent
Parent
Parent
Parent
Parent
Parent
Parent
Parent
Parent
Child
Child
Child
Child
Child
Child
Child
Child
Child


And here is the code that works:

Code:
int main()
{

        int file, a;
        pid_t pid;
        char buffer[10];
        int *status;

        pid = fork();

        file = open("/home/jamie/testfile", O_RDWR | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR);
        bzero ( buffer, 10 );


        if ( flock ( file, LOCK_EX) != 0 )
                perror("Lock error");

        if ( pid > 0 )
        strcpy ( buffer, "Parent\n");
        else
        strcpy ( buffer, "Child\n");


        for ( a=1; a < 10; a++){
        write (file, buffer, 10);
        sleep (1);
        }

        if ( flock ( file, LOCK_UN) != 0 )
                perror("Flock error");

        wait(status);
        return 0;
}
 
Back
Top