File Coverage

blib/lib/Sys/Syscall.pm
Criterion Covered Total %
statement 47 73 64.3
branch 6 16 37.5
condition 1 5 20.0
subroutine 16 22 72.7
pod 3 10 30.0
total 73 126 57.9


line stmt bran cond sub pod time code
1             # LICENSE: You're free to distribute this under the same terms as Perl itself.
2              
3             package Sys::Syscall;
4 3     3   51949 use strict;
  3         7  
  3         193  
5 3     3   3239 use POSIX qw(ENOSYS SEEK_CUR);
  3         28705  
  3         25  
6              
7             require Exporter;
8 3     3   4935 use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS $VERSION);
  3         15  
  3         603  
9              
10             $VERSION = "0.25";
11             @ISA = qw(Exporter);
12             @EXPORT_OK = qw(sendfile epoll_ctl epoll_create epoll_wait EPOLLIN EPOLLOUT EPOLLERR EPOLLHUP EPOLL_CTL_ADD EPOLL_CTL_DEL EPOLL_CTL_MOD);
13             %EXPORT_TAGS = (epoll => [qw(epoll_ctl epoll_create epoll_wait EPOLLIN EPOLLOUT EPOLLERR EPOLLHUP
14             EPOLL_CTL_ADD EPOLL_CTL_DEL EPOLL_CTL_MOD)],
15             sendfile => [qw(sendfile)],
16             );
17              
18 3     3   20 use constant EPOLLIN => 1;
  3         4  
  3         418  
19 3     3   16 use constant EPOLLOUT => 4;
  3         6  
  3         160  
20 3     3   15 use constant EPOLLERR => 8;
  3         7  
  3         343  
21 3     3   16 use constant EPOLLHUP => 16;
  3         5  
  3         210  
22 3     3   16 use constant EPOLL_CTL_ADD => 1;
  3         5  
  3         130  
23 3     3   14 use constant EPOLL_CTL_DEL => 2;
  3         5  
  3         138  
24 3     3   118 use constant EPOLL_CTL_MOD => 3;
  3         8  
  3         7003  
25              
26             our $loaded_syscall = 0;
27              
28             sub _load_syscall {
29             # props to Gaal for this!
30 0 0   0   0 return if $loaded_syscall++;
31             my $clean = sub {
32 0     0   0 delete @INC{qw
33             _h2ph_pre.ph sys/syscall.ph>};
34 0         0 };
35 0         0 $clean->(); # don't trust modules before us
36 0   0     0 my $rv = eval { require 'syscall.ph'; 1 } || eval { require 'sys/syscall.ph'; 1 };
37 0         0 $clean->(); # don't require modules after us trust us
38 0         0 return $rv;
39             }
40              
41             our ($sysname, $nodename, $release, $version, $machine) = POSIX::uname();
42              
43             our (
44             $SYS_epoll_create,
45             $SYS_epoll_ctl,
46             $SYS_epoll_wait,
47             $SYS_sendfile,
48             $SYS_readahead,
49             );
50              
51             if ($^O eq "linux") {
52             # whether the machine requires 64-bit numbers to be on 8-byte
53             # boundaries.
54             my $u64_mod_8 = 0;
55              
56             if ($machine =~ m/^i[3456]86$/) {
57             $SYS_epoll_create = 254;
58             $SYS_epoll_ctl = 255;
59             $SYS_epoll_wait = 256;
60             $SYS_sendfile = 187; # or 64: 239
61             $SYS_readahead = 225;
62             } elsif ($machine eq "x86_64") {
63             $SYS_epoll_create = 213;
64             $SYS_epoll_ctl = 233;
65             $SYS_epoll_wait = 232;
66             $SYS_sendfile = 40;
67             $SYS_readahead = 187;
68             } elsif ($machine eq "ppc64") {
69             $SYS_epoll_create = 236;
70             $SYS_epoll_ctl = 237;
71             $SYS_epoll_wait = 238;
72             $SYS_sendfile = 186; # (sys32_sendfile). sys32_sendfile64=226 (64 bit processes: sys_sendfile64=186)
73             $SYS_readahead = 191; # both 32-bit and 64-bit vesions
74             $u64_mod_8 = 1;
75             } elsif ($machine eq "ppc") {
76             $SYS_epoll_create = 236;
77             $SYS_epoll_ctl = 237;
78             $SYS_epoll_wait = 238;
79             $SYS_sendfile = 186; # sys_sendfile64=226
80             $SYS_readahead = 191;
81             $u64_mod_8 = 1;
82             } elsif ($machine eq "ia64") {
83             $SYS_epoll_create = 1243;
84             $SYS_epoll_ctl = 1244;
85             $SYS_epoll_wait = 1245;
86             $SYS_sendfile = 1187;
87             $SYS_readahead = 1216;
88             $u64_mod_8 = 1;
89             } elsif ($machine eq "alpha") {
90             # natural alignment, ints are 32-bits
91             $SYS_sendfile = 370; # (sys_sendfile64)
92             $SYS_epoll_create = 407;
93             $SYS_epoll_ctl = 408;
94             $SYS_epoll_wait = 409;
95             $SYS_readahead = 379;
96             $u64_mod_8 = 1;
97             } elsif ($machine =~ m/arm(v\d+)?.*l/) {
98             # ARM OABI
99             $SYS_epoll_create = 250;
100             $SYS_epoll_ctl = 251;
101             $SYS_epoll_wait = 252;
102             $SYS_sendfile = 187;
103             $SYS_readahead = 225;
104             $u64_mod_8 = 1;
105             } else {
106             # as a last resort, try using the *.ph files which may not
107             # exist or may be wrong
108             _load_syscall();
109             $SYS_epoll_create = eval { &SYS_epoll_create; } || 0;
110             $SYS_epoll_ctl = eval { &SYS_epoll_ctl; } || 0;
111             $SYS_epoll_wait = eval { &SYS_epoll_wait; } || 0;
112             $SYS_readahead = eval { &SYS_readahead; } || 0;
113             }
114              
115             if ($u64_mod_8) {
116             *epoll_wait = \&epoll_wait_mod8;
117             *epoll_ctl = \&epoll_ctl_mod8;
118             } else {
119             *epoll_wait = \&epoll_wait_mod4;
120             *epoll_ctl = \&epoll_ctl_mod4;
121             }
122             }
123              
124             elsif ($^O eq "freebsd") {
125             if ($ENV{FREEBSD_SENDFILE}) {
126             # this is still buggy and in development
127             $SYS_sendfile = 393; # old is 336
128             }
129             }
130              
131             ############################################################################
132             # sendfile functions
133             ############################################################################
134              
135             unless ($SYS_sendfile) {
136             _load_syscall();
137             $SYS_sendfile = eval { &SYS_sendfile; } || 0;
138             }
139              
140 1 50   1 1 11 sub sendfile_defined { return $SYS_sendfile ? 1 : 0; }
141              
142             if ($^O eq "linux" && $SYS_sendfile) {
143             *sendfile = \&sendfile_linux;
144             } elsif ($^O eq "freebsd" && $SYS_sendfile) {
145             *sendfile = \&sendfile_freebsd;
146             } else {
147             *sendfile = \&sendfile_noimpl;
148             }
149              
150             sub sendfile_noimpl {
151 0     0 0 0 $! = ENOSYS;
152 0         0 return -1;
153             }
154              
155             # C: ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
156             # Perl: sendfile($write_fd, $read_fd, $max_count) --> $actually_sent
157             sub sendfile_linux {
158 75     75 0 275430 return syscall(
159             $SYS_sendfile,
160             $_[0] + 0, # fd
161             $_[1] + 0, # fd
162             0, # don't keep track of offset. callers can lseek and keep track.
163             $_[2] + 0 # count
164             );
165             }
166              
167             sub sendfile_freebsd {
168 0     0 0 0 my $offset = POSIX::lseek($_[1]+0, 0, SEEK_CUR) + 0;
169 0         0 my $ct = $_[2] + 0;
170 0         0 my $sbytes_buf = "\0" x 8;
171 0         0 my $rv = syscall(
172             $SYS_sendfile,
173             $_[1] + 0, # fd (from)
174             $_[0] + 0, # socket (to)
175             $offset,
176             $ct,
177             0, # struct sf_hdtr *hdtr
178             $sbytes_buf, # off_t *sbytes
179             0); # flags
180 0 0       0 return $rv if $rv < 0;
181              
182              
183 0         0 my $set = unpack("L", $sbytes_buf);
184 0         0 POSIX::lseek($_[1]+0, SEEK_CUR, $set);
185 0         0 return $set;
186             }
187              
188              
189             ############################################################################
190             # epoll functions
191             ############################################################################
192              
193 1 50   1 1 225 sub epoll_defined { return $SYS_epoll_create ? 1 : 0; }
194              
195             # ARGS: (size) -- but in modern Linux 2.6, the
196             # size doesn't even matter (radix tree now, not hash)
197             sub epoll_create {
198 2 50   2 1 251792 return -1 unless defined $SYS_epoll_create;
199 2   50     27 my $epfd = eval { syscall($SYS_epoll_create, ($_[0]||100)+0) };
  2         42  
200 2 50       11 return -1 if $@;
201 2         7 return $epfd;
202             }
203              
204             # epoll_ctl wrapper
205             # ARGS: (epfd, op, fd, events_mask)
206             sub epoll_ctl_mod4 {
207 6     6 0 1711 syscall($SYS_epoll_ctl, $_[0]+0, $_[1]+0, $_[2]+0, pack("LLL", $_[3], $_[2], 0));
208             }
209             sub epoll_ctl_mod8 {
210 0     0 0 0 syscall($SYS_epoll_ctl, $_[0]+0, $_[1]+0, $_[2]+0, pack("LLLL", $_[3], 0, $_[2], 0));
211             }
212              
213             # epoll_wait wrapper
214             # ARGS: (epfd, maxevents, timeout (milliseconds), arrayref)
215             # arrayref: values modified to be [$fd, $event]
216             our $epoll_wait_events;
217             our $epoll_wait_size = 0;
218             sub epoll_wait_mod4 {
219             # resize our static buffer if requested size is bigger than we've ever done
220 3 100   3 0 11 if ($_[1] > $epoll_wait_size) {
221 2         3 $epoll_wait_size = $_[1];
222 2         8 $epoll_wait_events = "\0" x 12 x $epoll_wait_size;
223             }
224 3         1000911 my $ct = syscall($SYS_epoll_wait, $_[0]+0, $epoll_wait_events, $_[1]+0, $_[2]+0);
225 3         19 for ($_ = 0; $_ < $ct; $_++) {
226 3         14 @{$_[3]->[$_]}[1,0] = unpack("LL", substr($epoll_wait_events, 12*$_, 8));
  3         16  
227             }
228 3         19 return $ct;
229             }
230              
231             sub epoll_wait_mod8 {
232             # resize our static buffer if requested size is bigger than we've ever done
233 0 0   0 0   if ($_[1] > $epoll_wait_size) {
234 0           $epoll_wait_size = $_[1];
235 0           $epoll_wait_events = "\0" x 16 x $epoll_wait_size;
236             }
237 0           my $ct = syscall($SYS_epoll_wait, $_[0]+0, $epoll_wait_events, $_[1]+0, $_[2]+0);
238 0           for ($_ = 0; $_ < $ct; $_++) {
239             # 16 byte epoll_event structs, with format:
240             # 4 byte mask [idx 1]
241             # 4 byte padding (we put it into idx 2, useless)
242             # 8 byte data (first 4 bytes are fd, into idx 0)
243 0           @{$_[3]->[$_]}[1,2,0] = unpack("LLL", substr($epoll_wait_events, 16*$_, 12));
  0            
244             }
245 0           return $ct;
246             }
247              
248              
249             1;
250             __END__