通常,写入为追加句柄打开的句柄首先查找文件末尾。
如果文件是open
(2)ed with O_APPEND
,则文件偏移量在写入之前首先设置为文件末尾。文件偏移量的调整和写入操作作为原子步骤执行。
但是您在 AnyEvent::Handle 中看不到这一点。下面演示了这个问题:
$ perl -e'
use strict;
use warnings;
use AE qw( );
use AnyEvent::Handle qw( );
sub wait_for_drain {
my ($hdl) = @_;
my $drained = AE::cv();
$hdl->on_drain($drained);
$drained->recv();
}
my $qfn = "log";
unlink($qfn);
open(my $fh, ">>", $qfn) or die $!;
$fh->autoflush(1);
my $hdl = AnyEvent::Handle->new(
fh => $fh,
on_error => sub {
my ($hdl, $fatal, $msg) = @_;
if ($fatal) { die($msg); } else { warn($msg); }
},
);
$hdl->push_write("abc\n");
$hdl->push_write("def\n");
wait_for_drain($hdl);
print(-s $qfn, "\n");
truncate($qfn, 0);
print(-s $qfn, "\n");
$hdl->push_write("ghi\n");
wait_for_drain($hdl);
print(-s $qfn, "\n");
'
8
0
12
虽然以下说明了您应该看到的行为:
$ perl -e'
use strict;
use warnings;
my $qfn = "log";
unlink($qfn);
open(my $fh, ">>", $qfn) or die $!;
$fh->autoflush(1);
print($fh "abc\n");
print($fh "def\n");
print(-s $qfn, "\n");
truncate($qfn, 0);
print(-s $qfn, "\n");
print($fh "ghi\n");
print(-s $qfn, "\n");
'
8
0
4
问题是 AnyEvent::Handle 破坏了一些句柄的标志。上面的 AnyEvent 代码归结为以下内容:
$ perl -e'
use strict;
use warnings;
use Fcntl qw( F_SETFL O_NONBLOCK );
my $qfn = "log";
unlink($qfn);
open(my $fh, ">>", $qfn) or die $!;
$fh->autoflush(1);
fcntl($fh, F_SETFL, O_NONBLOCK);
print($fh "abc\n");
print($fh "def\n");
print(-s $qfn, "\n");
truncate($qfn, 0);
print(-s $qfn, "\n");
print($fh "ghi\n");
print(-s $qfn, "\n");
'
8
0
12
以下是 AnyEvent::Handle 应该做的事情:
$ perl -e'
use strict;
use warnings;
use Fcntl qw( F_GETFL F_SETFL O_NONBLOCK );
my $qfn = "log";
unlink($qfn);
open(my $fh, ">>", $qfn) or die $!;
$fh->autoflush(1);
my $flags = fcntl($fh, F_GETFL, 0)
or die($!);
fcntl($fh, F_SETFL, $flags | O_NONBLOCK)
or die($!);
print($fh "abc\n");
print($fh "def\n");
print(-s $qfn, "\n");
truncate($qfn, 0);
print(-s $qfn, "\n");
print($fh "ghi\n");
print(-s $qfn, "\n");
'
8
0
4
我已经提交了一个错误报告,但是模块的作者不愿意修复这个错误,所以我不得不推荐猴子补丁这种相当糟糕的做法。将以下内容添加到您的程序中:
use AnyEvent qw( );
use AnyEvent::Util qw( );
use Fcntl qw( );
BEGIN {
if (!AnyEvent::WIN32) {
my $fixed_fh_nonblocking = sub($$) {
my $flags = fcntl($_[0], Fcntl::F_GETFL, 0)
or return;
$flags = $_[1]
? $flags | AnyEvent::O_NONBLOCK
: $flags & ~AnyEvent::O_NONBLOCK;
fcntl($_[0], AnyEvent::F_SETFL, $flags);
};
no warnings "redefine";
*AnyEvent::Util::fh_nonblocking = $fixed_fh_nonblocking;
}
}
使用此修复程序,您的程序将正常运行
$ perl -e'
use strict;
use warnings;
use AE qw( );
use AnyEvent qw( );
use AnyEvent::Handle qw( );
use AnyEvent::Util qw( );
use Fcntl qw( );
BEGIN {
if (!AnyEvent::WIN32) {
my $fixed_fh_nonblocking = sub($$) {
my $flags = fcntl($_[0], Fcntl::F_GETFL, 0)
or return;
$flags = $_[1]
? $flags | AnyEvent::O_NONBLOCK
: $flags & ~AnyEvent::O_NONBLOCK;
fcntl($_[0], AnyEvent::F_SETFL, $flags);
};
no warnings "redefine";
*AnyEvent::Util::fh_nonblocking = $fixed_fh_nonblocking;
}
}
sub wait_for_drain {
my ($hdl) = @_;
my $drained = AE::cv();
$hdl->on_drain($drained);
$drained->recv();
}
my $qfn = "log";
unlink($qfn);
open(my $fh, ">>", $qfn) or die $!;
$fh->autoflush(1);
my $hdl = AnyEvent::Handle->new(
fh => $fh,
on_error => sub {
my ($hdl, $fatal, $msg) = @_;
if ($fatal) { die($msg); } else { warn($msg); }
},
);
$hdl->push_write("abc\n");
$hdl->push_write("def\n");
wait_for_drain($hdl);
print(-s $qfn, "\n");
truncate($qfn, 0);
print(-s $qfn, "\n");
$hdl->push_write("ghi\n");
wait_for_drain($hdl);
print(-s $qfn, "\n");
'
8
0
4