File Coverage

blib/lib/IO/FDSaver.pm
Criterion Covered Total %
statement 10 10 100.0
branch 1 2 50.0
condition n/a
subroutine 5 5 100.0
pod 2 2 100.0
total 18 19 94.7


line stmt bran cond sub pod time code
1             package IO::FDSaver;
2              
3 1     1   698 use strict;
  1         2  
  1         29  
4 1     1   5 use warnings;
  1         1  
  1         173  
5              
6             our $VERSION = '0.01';
7              
8             =encoding utf-8
9              
10             =head1 NAME
11              
12             IO::FDSaver - Save file descriptors from Perl’s garbage collection.
13              
14             =head1 SYNOPSIS
15              
16             my $saver = IO::FDSaver->new();
17              
18             my $fd_from_xsub = 5;
19              
20             # $fh can be garbage-collected, but Perl will never close FD 5.
21             my $fh = $saver->get_fh( $fd_from_xsub );
22              
23             =head1 DESCRIPTION
24              
25             Perl’s ability to create a filehandle from a given file descriptor
26             is critical for advanced IPC functionality like accepting a file descriptor
27             across an C or via UNIX socket (i.e., SCM_RIGHTS).
28              
29             It’s also useful when interfacing with C libraries (i.e., via XSUBs), but
30             in this context there’s a catch: Perl expects to “own” all of its file
31             handles’ file descriptors. So if Perl garbage-collects its last file handle
32             that refers to a given file descriptor, Perl will close that file descriptor.
33             Thus, your C code—which has no idea there’s this Perl thing calling into
34             it—will suddenly start getting EBADF when trying to use its file
35             descriptors. These errors can be confusing and time-consuming to fix.
36              
37             The present module solves this problem by retaining an index of file descriptors
38             and Perl file handles. As long as a given instance of this class survives,
39             Perl will never auto-close the file descriptors given to that object
40             because there will always remain at least one file handle that refers to each
41             file descriptor.
42              
43             (NB: File descriptors I given to such an object will behave as usual.)
44              
45             =head1 METHODS
46              
47             =head2 $obj = I->new()
48              
49             Instantiates this class.
50              
51             =cut
52              
53 2     2 1 2736 sub new { return bless {}, shift }
54              
55             =head2 $fh = I->get_fh( $FD )
56              
57             This module’s “workhorse” method: takes in a file descriptor and returns
58             a file handle for it.
59              
60             The returned filehandle will be opened read/write—even if the underlying file
61             descriptor is read-only or write-only. The caller is expected to discipline
62             itself not to misuse the returned filehandle.
63              
64             (There might be virtue in allowing a mode to pass to the underlying C;
65             file a feature request if you’d like that.)
66              
67             =cut
68              
69             sub get_fh {
70 2     2 1 921 return $_[0]->{ $_[1] } = _create( $_[1] );
71             }
72              
73             sub _create {
74              
75             # The mode doesn’t seem to make a difference as long as it
76             # expresses read/write.
77 2 50   2   57 open my $s, '+>>&=' . $_[0] or die "FD ($_[0]) to Perl FH failed: $!";
78              
79 2         15 $s;
80             }
81              
82             =head1 AUTHOR & COPYRIGHT
83              
84             Copyright 2021 Gasper Software Consulting
85              
86             =head1 LICENSE
87              
88             This library is licensed under the same license as Perl.
89              
90             =cut
91              
92             1;