File Coverage

blib/lib/Hypersonic/Event/Select.pm
Criterion Covered Total %
statement 31 38 81.5
branch 3 8 37.5
condition n/a
subroutine 16 18 88.8
pod 13 14 92.8
total 63 78 80.7


line stmt bran cond sub pod time code
1             package Hypersonic::Event::Select;
2              
3 3     3   227850 use strict;
  3         5  
  3         109  
4 3     3   13 use warnings;
  3         4  
  3         136  
5 3     3   46 use 5.010;
  3         10  
6              
7 3     3   17 use parent 'Hypersonic::Event::Role';
  3         5  
  3         28  
8              
9             our $VERSION = '0.15';
10              
11 2     2 1 1904 sub name { 'select' }
12              
13 7     7 1 1987 sub available { 1 } # Always available - most portable
14              
15             sub includes {
16 1     1 1 1296 my $class = shift;
17 1 50       7 if ($^O eq 'MSWin32') {
18 0         0 return <<'C';
19             #include
20             #include
21             #pragma comment(lib, "ws2_32.lib")
22             C
23             }
24 1         4 return '#include ';
25             }
26              
27             sub defines {
28 2     2 1 2656 return <<'C';
29             #define EV_BACKEND_SELECT 1
30              
31             /* Increase FD_SETSIZE before including headers on some systems */
32             #ifndef FD_SETSIZE
33             #define FD_SETSIZE 1024
34             #endif
35              
36             #ifndef MAX_EVENTS
37             #define MAX_EVENTS FD_SETSIZE
38             #endif
39             C
40             }
41              
42 1     1 1 1340 sub event_struct { 'fd_set' } # Not really used as array
43              
44 1     1 1 1300 sub extra_cflags { '' }
45 1 50   1 1 8 sub extra_ldflags { $^O eq 'MSWin32' ? '-lws2_32' : '' }
46              
47             # select() needs to track all fds and rebuild fd_sets each iteration
48             sub gen_create {
49 1     1 1 1387 my ($class, $builder, $listen_fd_var) = @_;
50              
51 1         5 $builder->comment('select() backend - most portable, lowest performance')
52             ->line('fd_set master_read_fds;')
53             ->line('FD_ZERO(&master_read_fds);')
54             ->line("FD_SET($listen_fd_var, &master_read_fds);")
55             ->line("int max_fd = $listen_fd_var;")
56             ->line('int ev_fd = 0;') # Dummy - select doesn't use event fd
57             ->blank;
58              
59             # Windows requires WSAStartup
60 1 50       48 if ($^O eq 'MSWin32') {
61             # gen_create is inlined into the void XS hypersonic_run_event_loop;
62             # use croak instead of `return -1;` which trips GCC 14+
63             # -Wreturn-mismatch (now an error).
64 0         0 $builder->line('WSADATA wsa_data;')
65             ->if('WSAStartup(MAKEWORD(2,2), &wsa_data) != 0')
66             ->line('croak("WSAStartup failed");')
67             ->endif;
68             }
69             }
70              
71             # Add fd to master set
72             sub gen_add {
73 1     1 1 2135 my ($class, $builder, $loop_var, $fd_var) = @_;
74              
75 1         15 $builder->line("FD_SET($fd_var, &master_read_fds);")
76             ->if("$fd_var > max_fd")
77             ->line("max_fd = $fd_var;")
78             ->endif;
79             }
80              
81             # Remove fd from master set
82             sub gen_del {
83 1     1 1 1148 my ($class, $builder, $loop_var, $fd_var) = @_;
84              
85 1         6 $builder->line("FD_CLR($fd_var, &master_read_fds);")
86             ->comment('Note: max_fd not updated (would need to scan)');
87             }
88              
89             # Wait using select() - must copy fd_set each time
90             sub gen_wait {
91 1     1 1 1085 my ($class, $builder, $loop_var, $events_var, $count_var, $timeout_var) = @_;
92              
93 1         8 $builder->comment('Copy master set - select() modifies it')
94             ->line('fd_set read_fds = master_read_fds;')
95             ->blank
96             ->line('struct timeval tv;')
97             ->line("tv.tv_sec = $timeout_var / 1000;")
98             ->line("tv.tv_usec = ($timeout_var % 1000) * 1000;")
99             ->blank
100             ->line('int select_result = select(max_fd + 1, &read_fds, NULL, NULL, &tv);')
101             ->if('select_result < 0')
102             ->if('errno == EINTR')
103             ->line('continue;')
104             ->endif
105             ->line('perror("select");')
106             ->line('break;')
107             ->endif
108             ->if('select_result == 0')
109             ->line('continue;') # Timeout
110             ->endif
111             ->blank
112             ->comment('Store read_fds pointer for iteration')
113             ->line("fd_set* $events_var = &read_fds;")
114             ->line("int $count_var = max_fd + 1;"); # We check all fds up to max
115             }
116              
117             # select() iteration is different - check each fd with FD_ISSET
118             sub gen_get_fd {
119 1     1 1 2388 my ($class, $builder, $events_var, $index_var, $fd_var) = @_;
120              
121 1         7 $builder->line("int $fd_var = $index_var;") # fd IS the index for select
122             ->if("!FD_ISSET($fd_var, $events_var)")
123             ->line('continue;') # Skip fds without events
124             ->endif;
125             }
126              
127             # Windows cleanup
128             sub gen_cleanup {
129 0     0 1   my ($class, $builder) = @_;
130 0 0         if ($^O eq 'MSWin32') {
131 0           $builder->line('WSACleanup();');
132             }
133             }
134              
135             # Future/Pool integration - add pool notify fd to master set
136             sub gen_add_pool_notify {
137 0     0 0   my ($class, $builder, $loop_var, $notify_fd_var) = @_;
138              
139 0           $builder->line("/* Add pool notify fd to select master set */")
140             ->line("FD_SET($notify_fd_var, &master_read_fds);")
141             ->if("$notify_fd_var > max_fd")
142             ->line("max_fd = $notify_fd_var;")
143             ->endif;
144             }
145              
146             1;
147              
148             __END__