简体   繁体   中英

File locking with Fcntl: Baffling bug involving 'use' and 'require'

The following Perl script outputs "SUCCESS" as you'd expect:

use Fcntl qw(:DEFAULT :flock);
sysopen(LF, "test.txt", O_RDONLY | O_CREAT) or die "SYSOPEN FAIL: $!";
if(flock(LF, LOCK_EX)) { print "SUCCESS.\n"; }
else { print "FAIL: $!\n"; }

But now, replace that first line with

require "testlib.pl";

where testlib.pl contains

use Fcntl qw(:DEFAULT :flock);

1;

Now, strangely enough, the script fails, like so:

FAIL: Bad file descriptor

The question: Why?

ADDED:

And now that I know why -- thanks! -- I'm wondering what is the best way to deal with this:

  1. Just do the use Fcntl twice, once in the main script and once in the required library (both the main script and the library need it).
  2. Replace O_RDONLY with &O_RDONLY, etc.
  3. Replace O_RDONLY with O_RDONLY(), etc.
  4. Something else?

By foregoing use , you deprive the Perl parser of the knowledge that O_RDONLY et al. are parameterless subroutines. You have to be a bit more verbose in that situation:

sysopen(LF, "test.txt", O_RDONLY() | O_CREAT()) or die "SYSOPEN FAIL: $!";
if(flock(LF, LOCK_EX())) { print "SUCCESS.\n"; }

EDIT: To elaborate a bit further, without the parentheses, the O_RDONLY and O_CREAT were being interpreted as barewords (strings), which don't behave as you'd expect when binary-or'ed together:

$ perl -le 'print O_RDONLY | O_CREAT'
O_SVOO\Y

(The individual characters are being bitwise or'ed togther.)

In this case, the string "O_SVOO\\Y" (or whatever it is on your system) was being interpreted as the number 0 to sysopen , which would therefore still work as long as O_RDONLY is 0 (as is typical) and the file already existed (so the O_CREAT was superfluous). But fcntl is apparently not as forgiving with non-numeric arguments:

$ perl -e 'flock STDOUT, "LOCK_EX" or die "Failed: $!"'
Failed: Bad file descriptor at -e line 1.

Similarly:

$ perl -e 'flock STDOUT, LOCK_EX or die "Failed: $!"'
Failed: Bad file descriptor at -e line 1.

However:

$ perl -e 'use Fcntl qw(:flock); flock STDOUT, LOCK_EX or die "Failed: $!"'
(no output)

Finally, note that use strict provides many helpful clues.

The line use Fcntl qw(:DEFAULT :flock); is not just loading the Fcntl library for you, but also exporting some symbols into your script's namespace. If you move that to a different scope, then the constants O_RDONLY, O_CREAT, LF, and LOCK_EX are no longer available to you, and your code won't do the same thing [however you could still reach them, if you know what namespace they ended up in -- since it was a script that did the export, you could call &main::NAME or simply &NAME, but then you have to be aware of what another file is doing with its code, which is not very clean] .

This is described in the documentation under EXPORTED SYMBOLS :

By default your system's F_* and O_* constants (eg, F_DUPFD and O_CREAT) and the FD_CLOEXEC constant are exported into your namespace.

You can request that the flock() constants (LOCK_SH, LOCK_EX, LOCK_NB and LOCK_UN) be provided by using the tag ":flock". See Exporter.

If you add the lines

use strict;
use warnings;

to the top of your script, you will get more informative error messages such as "Name "main::O_RDONLY" used only once: possible type at line ...", which would give you a clue that these constants definitions are no longer visible.

Edit: in response to your question, the best practice would be #1, to include the use statement in every file that needs it. See perldoc -f use -- the Fcntl library is only included once, but the import() call is made every time it is needed, which is what you want.

use is equivalent to:

BEGIN { require Module; Module->import( LIST ); }

guaranteeing that the import functions are available before the code starts executing. Whe you replace use with require, it simply reads the code in at the lexical point in the program where it exists.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM