perlubuntufile-descriptorfcntl

Perl Fcntl calls to F_SETPIPE_SZ or F_GETPIPE_SZ throws "Bad file descriptor"


Background:

I am trying to write a small script that logs JSONS concurrently, if the file sizes are small, things are ok. But when file sizes are large, processes start to overwrite each other. This SO post is helpful to point in the right direction: PIPE_BUFF. Windows seems to have it set to 1024, on linux larger How big is the PIPE_BUFF?

PS: I am on WSL2 Ubuntu 20.04

Issue

I tried setting the PIPE_BUFF value using the Fcntl constant F_SETPIPE_SZ, however I was not successful:

#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Data::Dumper qw(Dumper);
use File::Basename qw(basename dirname);
use File::Spec qw(catfile file_name_is_absolute catdir);
use feature qw(say current_sub);
use Cwd qw(abs_path);
use lib dirname(abs_path $0);
use Fcntl qw(F_SETPIPE_SZ F_GETPIPE_SZ F_GETFL F_GETFD F_SETFL O_NONBLOCK O_WRONLY O_APPEND O_CREAT O_RDWR F_DUPFD F_SETOWN F_GETOWN);

open(my $fileH,">", File::Spec -> catfile(dirname(__FILE__), "blabla.txt"));
#does F_GETFD work?
my $val = fcntl($fileH, F_GETFD, 0);
say $val; #outputs 1, works
#does F_GETFL work?
my $val2 = fcntl($fileH, F_GETFL, 0);
say $val2; #outputs 32769, works
#does F_GETOWN work?
my $val3 = fcntl($fileH, F_GETOWN, 0);
say $val3; #outputs 0 but true, so it works too.
my $pid = +$$;#process id
#does F_SETOWN work?
my $val4 = fcntl($fileH, F_SETOWN, $pid) or die("error: $!");;
say $val4; #outputs 0 but true
#does getting pipe buffer work?
my $val5 = fcntl($fileH, F_GETPIPE_SZ, 0) or die("error: $!"); #"Bad file descriptor"
say $val5; #Use of uninitialized..so $val5 is undef, did not work
#does setting pipe buffer work?
my $val6 = fcntl($fileH, F_SETPIPE_SZ, 1000 * 1000) or die("error: $!"); #"Bad file descriptor"
say $val6; #undef

Several constants like F_GETFD or F_GETFL works, so I am guessing Fcntl is operating properly.

However F_SETPIPE_SZ and some others does not seem to work at all. Passing fcntl fileno($fileH) instead of $fileH results in "unopened file handle" error, so it does not change anything as well.

What is the underlying reason for this?


Solution

  • The Linux fcntl() flags F_GETPIPE_SZ and F_SETPIPE_SZ are, as their names suggest, specific to pipes. You're trying to use them with a regular file, hence the failures.

    For clarity:

    #!/usr/bin/env perl                                                                                                                                                                                                                               
    use strict;
    use warnings;
    use feature qw/say/;
    use Fcntl qw/F_GETPIPE_SZ/;
    
    open my $file, ">", "/tmp/foo.txt" or die "Unable to open /tmp/foo.txt: $!\n";
    pipe my $reader, my $writer or die "Unable to create pipe: $!\n";
    
    if (my $size = fcntl($file, F_GETPIPE_SZ, 0)) {
        say "filehandle size: $size";
    } else {
        say "fcntl of file failed: $!";
    }
    
    if (my $size = fcntl($reader, F_GETPIPE_SZ, 0)) {
        say "pipe size: $size";
    } else {
        say "fcntl of pipe filed: $!";
    }
    

    Possible output:

    fcntl of file failed: Bad file descriptor
    pipe size: 65536