File Coverage

blib/lib/Linux/Event/Backend/Epoll.pm
Criterion Covered Total %
statement 123 134 91.7
branch 23 52 44.2
condition 1 3 33.3
subroutine 20 21 95.2
pod 7 7 100.0
total 174 217 80.1


line stmt bran cond sub pod time code
1             package Linux::Event::Backend::Epoll;
2 14     14   93092 use v5.36;
  14         41  
3 14     14   58 use strict;
  14         21  
  14         250  
4 14     14   40 use warnings;
  14         20  
  14         807  
5              
6             our $VERSION = '0.012';
7              
8 14     14   59 use Carp qw(croak);
  14         20  
  14         706  
9 14     14   446 use Linux::Event::XS ();
  14         50  
  14         314  
10              
11 14     14   89 use constant READABLE => 0x01;
  14         23  
  14         900  
12 14     14   131 use constant WRITABLE => 0x02;
  14         32  
  14         577  
13 14     14   82 use constant PRIO => 0x04;
  14         23  
  14         494  
14 14     14   47 use constant RDHUP => 0x08;
  14         31  
  14         473  
15 14     14   51 use constant ET => 0x10;
  14         109  
  14         528  
16 14     14   72 use constant ONESHOT => 0x20;
  14         17  
  14         555  
17 14     14   61 use constant ERR => 0x40;
  14         19  
  14         494  
18 14     14   52 use constant HUP => 0x80;
  14         79  
  14         12015  
19              
20 20     20 1 148546 sub new ($class, %args) {
  20         31  
  20         23  
  20         25  
21             # Optional backend defaults (not part of Loop's public API):
22 20         48 my $edge = delete $args{edge};
23 20         31 my $oneshot = delete $args{oneshot};
24 20 50       44 croak "unknown args: " . join(", ", sort keys %args) if %args;
25              
26 20         449 my $ep = Linux::Event::XS::epoll_new();
27              
28 20 50       409 return bless {
    50          
29             ep => $ep, # XS epoll fd + reusable event buffer
30             watch => Linux::Event::XS::registry_new(), # fd -> XS backend watch record
31             edge => $edge ? 1 : 0,
32             oneshot => $oneshot ? 1 : 0,
33             }, $class;
34             }
35              
36 2     2 1 2 sub name ($self) { return 'epoll' }
  2         3  
  2         2  
  2         9  
37              
38 1     1 1 20 sub watch ($self, $fh, $mask, $cb, %opt) {
  1         2  
  1         2  
  1         2  
  1         2  
  1         4  
  1         1  
39 1 50       5 croak "fh is required" if !$fh;
40 1 50       4 croak "mask is required" if !defined $mask;
41 1 50       3 croak "cb is required" if !$cb;
42 1 50       4 croak "cb must be a coderef" if ref($cb) ne 'CODE';
43              
44 1         4 my $fd = fileno($fh);
45 1 50       3 croak "fh has no fileno" if !defined $fd;
46 1         3 $fd = int($fd);
47              
48 1 50       12 croak "fd already watched: $fd" if Linux::Event::XS::registry_get($self->{watch}, $fd);
49              
50 1         6 my $events = _mask_to_events($self, $mask);
51             my $rec = Linux::Event::XS::backend_watch_new(
52             'Linux::Event::XS::BackendWatch',
53             $fd,
54             $fh,
55             $cb,
56             int($mask),
57             $opt{_loop},
58             $opt{tag},
59 1         12 );
60              
61 1         36 Linux::Event::XS::epoll_add($self->{ep}, $fd, $events);
62 1         10 Linux::Event::XS::registry_set($self->{watch}, $fd, $rec);
63              
64 1         4 return $fd;
65             }
66              
67              
68 1     1 1 2 sub watch_watcher ($self, $fh, $mask, $watcher, %opt) {
  1         1  
  1         2  
  1         2  
  1         1  
  1         2  
  1         2  
69 1 50       3 croak "fh is required" if !$fh;
70 1 50       2 croak "mask is required" if !defined $mask;
71 1 50       3 croak "watcher is required" if !$watcher;
72              
73 1         2 my $fd = fileno($fh);
74 1 50       3 croak "fh has no fileno" if !defined $fd;
75 1         2 $fd = int($fd);
76              
77 1 50       3 croak "fd already watched: $fd" if Linux::Event::XS::registry_get($self->{watch}, $fd);
78              
79 1         3 my $events = _mask_to_events($self, $mask);
80             my $rec = Linux::Event::XS::backend_watch_new_watcher(
81             'Linux::Event::XS::BackendWatch',
82             $fd,
83             $fh,
84             $watcher,
85             int($mask),
86             $opt{_loop},
87             $opt{tag},
88 1         6 );
89              
90 1         8 Linux::Event::XS::epoll_add($self->{ep}, $fd, $events);
91 1         3 Linux::Event::XS::registry_set($self->{watch}, $fd, $rec);
92              
93 1         3 return $fd;
94             }
95              
96 2     2 1 52 sub modify ($self, $fh_or_fd, $mask, %opt) {
  2         3  
  2         3  
  2         3  
  2         3  
  2         3  
97 2 50       5 croak "mask is required" if !defined $mask;
98              
99 2 50       6 my $fd = ref($fh_or_fd) ? fileno($fh_or_fd) : $fh_or_fd;
100 2 50       5 return 0 if !defined $fd;
101 2         3 $fd = int($fd);
102              
103 2 50       33 my $rec = Linux::Event::XS::registry_get($self->{watch}, $fd) or return 0;
104              
105 2 50       7 my $loop = exists $opt{_loop} ? $opt{_loop} : undef;
106 2 50       4 my $tag = exists $opt{tag} ? $opt{tag} : undef;
107             Linux::Event::XS::backend_watch_set_loop_tag($rec, $loop, $tag)
108 2 50 33     22 if exists($opt{_loop}) || exists($opt{tag});
109              
110 2         4 my $new_mask = int($mask);
111 2         6 Linux::Event::XS::backend_watch_set_mask($rec, $new_mask);
112              
113 2         5 my $events = _mask_to_events($self, $new_mask);
114 2         15 Linux::Event::XS::epoll_modify($self->{ep}, $fd, $events);
115 2         8 return 1;
116             }
117              
118 0     0 1 0 sub unwatch ($self, $fh_or_fd) {
  0         0  
  0         0  
  0         0  
119 0 0       0 my $fd = ref($fh_or_fd) ? fileno($fh_or_fd) : $fh_or_fd;
120 0 0       0 return 0 if !defined $fd;
121 0         0 $fd = int($fd);
122              
123 0 0       0 my $rec = Linux::Event::XS::registry_get($self->{watch}, $fd) or return 0;
124 0         0 Linux::Event::XS::epoll_delete($self->{ep}, $fd);
125 0         0 Linux::Event::XS::registry_delete($self->{watch}, $fd);
126 0         0 return 1;
127             }
128              
129 32     32 1 107 sub run_once ($self, $loop, $timeout_s = undef) {
  32         49  
  32         37  
  32         62  
  32         35  
130             # XS owns the epoll fd and reuses a fixed epoll_event buffer across waits.
131 32         768567 return Linux::Event::XS::epoll_wait_dispatch($self->{ep}, $self->{watch}, $timeout_s);
132             }
133              
134 4     4   6 sub _mask_to_events ($self, $mask) {
  4         7  
  4         8  
  4         5  
135 4         30 $mask = int($mask);
136 4 50       16 $mask |= ET if $self->{edge};
137 4 50       13 $mask |= ONESHOT if $self->{oneshot};
138 4         8 return $mask;
139             }
140              
141             1;
142              
143             __END__