How do you lock a file BEFORE changes are made?

J

J. Romano

Hi,

Since flock() calls have become such a hot topic recently, I've
been wondering how I can lock a file before any changes are made. Let
me provide an example:

Say I have a file with one-thousand lines of text. (If you want to
follow along, you can quickly create one with the command:

perl -le "print 'text line' foreach (1 .. 1000)" > file.txt

). Now I invoke the "interactive Perl interpreter" with the line:

perl -de 1

Now I want to open the file for writing, lock it, and then change
its contents once I acquire the lock. Normally I would do it like
this:

open(FILE, ">file.txt") or warn "Could not open: $!";
flock(FILE, LOCK_EX);
print FILE "New text lines\n" x 50;
close(FILE); # closes the file and releases the lock

This looks acceptable in that I don't modify anything until AFTER I
acquire the lock, but when I execute that first line in the
interpreter (and before I execute the next line that has the flock()
call) I can see in another window that the file's contents get cleared
out.

But that's not what I want! I don't want the file to be modified
in any way until I get the lock, but I can't get the lock until after
I open the file, and by that time the contents have already been
modified!

Is there any way to open a file for writing that won't change its
contents before I use flock()?

In case anyone is interested, here is my "perl -v" output:

This is perl, v5.8.2 built for MSWin32-x86-multi-thread
(with 25 registered patches, see perl -V for more detail)

I did find one work-around... instead of opening the file with ">",
I can open it with "+<", then use flock(), then seek to the beginning
of the file, like this:

open(FILE, "+<file.txt") or warn "Could not open: $!";
flock(FILE, LOCK_EX);
seek(FILE, 0, 0); # go to the beginning of the file
print FILE "New text lines\n" x 50;
truncate(FILE, tell(FILE)); # remove extra old data
close(FILE); # closes the file and releases the lock

Not only is this approach unfavorable because it uses extra lines of
code that seem unnecessary, but according to "perldoc -f truncate",
calling truncate() "produces a fatal error if truncate isn't
implemented on your system," which makes me think that it's not all
that portable.

Does anyone have a better way to lock a file before it's modified?

Thanks in advance.

-- Jean-Luc
 
D

David K. Wall

But that's not what I want! I don't want the file to be
modified in any way until I get the lock, but I can't get the
lock until after I open the file, and by that time the contents
have already been modified!

Maybe you want to open the file for appending or perhaps read
and write instead of just opening it for writing?
 
G

Gunnar Hjalmarsson

J. Romano said:
Is there any way to open a file for writing that won't change its
contents before I use flock()?

I did find one work-around... instead of opening the file with ">",
I can open it with "+<", then use flock(), then seek to the
beginning of the file,

I wouldn't call that a work-around; It's one of the available
solutions. It requires that the file already exists, though. If that's
not always the case, the sysopen() function offers more flexibility.

You can also open a separate lockfile and flock() that before opening
the actual file.
 
J

Jay Tilton

(e-mail address removed) (J. Romano) wrote:

: Since flock() calls have become such a hot topic recently, I've
: been wondering how I can lock a file before any changes are made. Let
: me provide an example:

[snip]

: I did find one work-around... instead of opening the file with ">",
: I can open it with "+<", then use flock(), then seek to the beginning
: of the file, like this:
:
: open(FILE, "+<file.txt") or warn "Could not open: $!";
: flock(FILE, LOCK_EX);
: seek(FILE, 0, 0); # go to the beginning of the file
: print FILE "New text lines\n" x 50;
: truncate(FILE, tell(FILE)); # remove extra old data
: close(FILE); # closes the file and releases the lock
:
: Not only is this approach unfavorable because it uses extra lines of
: code that seem unnecessary, but according to "perldoc -f truncate",
: calling truncate() "produces a fatal error if truncate isn't
: implemented on your system," which makes me think that it's not all
: that portable.

Did you not see the same note attached to "perldoc -f flock"? It too will
cause a fatal error on a system where it is not implemented.

How much portability do you really need? Is it worth jumping through hoops
to make your program accommodate every platform that can run Perl?
 
J

J. Romano

(e-mail address removed) (J. Romano) wrote:
:
: Not only is this approach unfavorable because it uses extra lines of
: code that seem unnecessary, but according to "perldoc -f truncate",
: calling truncate() "produces a fatal error if truncate isn't
: implemented on your system," which makes me think that it's not all
: that portable.

How much portability do you really need? Is it worth jumping through hoops
to make your program accommodate every platform that can run Perl?

I would just like to know if there is an easy way to lock a file
before it gets truncated. If there isn't, then apparently flock()
isn't meant to be used with an open() statement that uses ">" (or
"+>"). This surprises me a little, because nowhere in "perldoc -f
flock" does it say this. As a result, anyone who doesn't realize this
will get a nasty surprise when they try flock()ing a file they
open()ed with ">".

As for jumping through hoops, I just want to know how it's done,
especially if there is an easier and better way to do it. I've only
had this situation come up once, and I took the approach I stated in
an earlier post (that is, open()ing with "+<", seek()ing to the start
of the file, and then truncate()ing the file) and it worked great at
the time.

But should I have to do the same thing in the future, should I
stick to the same method, or is there a better way to do this that I'm
just not aware of yet?

I tend to avoid lock-files because, when removed, they can cause
race conditions. I think I can avoid a race condition if I just
create a zero-length lock-file whose sole purpose is to be locked by
any process wishing to edit another file. This lock-file would never
be deleted, because if it was, the possibility exists that it might be
removed while another process had it locked (which is not a good
thing). And if I used this approach, I would have to have this
lock-file hang around for good since it would never be deleted.

Like I said, I just want to know how to lock a file for writing
before it gets truncated for the next time I have to do it. I could
stick with the approach I've used before, and if for some reason it's
not portable enough to be used on the platform I will be using at the
time, I can try the lock-file approach. (And if that doesn't work, I
don't know what I'll do...)

-- Jean-Luc
 
C

ctcgag

(e-mail address removed) (Jay Tilton) wrote in message


I would just like to know if there is an easy way to lock a file
before it gets truncated. If there isn't, then apparently flock()
isn't meant to be used with an open() statement that uses ">" (or
"+>"). This surprises me a little, because nowhere in "perldoc -f
flock" does it say this.

Well, the damage is already done by the time flock gets to it, so
perldoc -f flock wouldn't be a very intuitive place to put such a warning.
Should it also warn that flock can't be used as a scalpel during brain
surgery?

It is given in perldoc perlopentut, which seems about right.
As a result, anyone who doesn't realize this
will get a nasty surprise when they try flock()ing a file they
open()ed with ">".

Nasty, yes. Surprise? shouldn't be. There is no reason to think that
open is going look ahead in every possible execution path of your code to
see if you later flock the filehandle (but of course before you first write
to it), and then change the way it behaves based on that. That is
unreasonable.
As for jumping through hoops, I just want to know how it's done,
especially if there is an easier and better way to do it. I've only
had this situation come up once, and I took the approach I stated in
an earlier post (that is, open()ing with "+<", seek()ing to the start
of the file, and then truncate()ing the file) and it worked great at
the time.

Put that code in a subroutine library, and use it. (no, actually, take
the code out of perlopentut and use that. It handles the case that the
file you want to lock doesn't even exist yet.)

Like I said, I just want to know how to lock a file for writing
before it gets truncated for the next time I have to do it. I could
stick with the approach I've used before, and if for some reason it's
not portable enough to be used on the platform I will be using at the
time, I can try the lock-file approach. (And if that doesn't work, I
don't know what I'll do...)

Not use that platform :)

Xho
 
B

Bob Walton

J. Romano said:
....
I would just like to know if there is an easy way to lock a file
before it gets truncated. If there isn't, then apparently flock()

The least-hassle approach IMO is to simply use another file (termed the
"lock file") to establish the lock. That file may be empty or have
contents which don't matter. Then your programs can open that file, for
write if need be, and establish the appropriate lock. Then open and
read or write to your real file (or do whatever it is to whatever it is
that needs exclusive or shared access). When you're done, close the
real file, and then close the lock file. That has proven to be highly
portable between OS's, to work very reliably, and to be very simple to
both use and understand.

....
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top