File Coverage

blib/lib/POE/Loop/Glib.pm
Criterion Covered Total %
statement 25 27 92.5
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 34 36 94.4


line stmt bran cond sub pod time code
1             # Glib event loop bridge for POE::Kernel.
2              
3             # Empty package to appease perl.
4             package POE::Loop::Glib;
5 1     1   877 use strict;
  1         2  
  1         40  
6 1     1   4 use warnings;
  1         3  
  1         33  
7              
8 1     1   1784 use POE::Kernel; # for MakeMaker
  1         99534  
  1         10  
9 1     1   76520 use vars qw($VERSION);
  1         2  
  1         43  
10             $VERSION = '0.038';
11              
12             # Include common signal handling.
13 1     1   6 use POE::Loop::PerlSignals;
  1         3  
  1         23  
14              
15             # Everything plugs into POE::Kernel.
16             package # Hide from Pause
17             POE::Kernel;
18 1     1   4 use strict;
  1         2  
  1         33  
19 1     1   5 use warnings;
  1         2  
  1         32  
20 1     1   4 no warnings 'redefine';
  1         3  
  1         32  
21              
22             # Fixes RT#55279
23 1     1   461 use Glib;
  0            
  0            
24              
25             my $_watcher_timer;
26             my $_idle_timer;
27             my @fileno_watcher;
28              
29             # Loop construction and destruction.
30              
31             sub loop_finalize {
32             foreach my $fd (0..$#fileno_watcher) {
33             next unless defined $fileno_watcher[$fd];
34             foreach my $mode (MODE_RD, MODE_WR, MODE_EX) {
35             POE::Kernel::_warn(
36             "Mode $mode watcher for fileno $fd is defined during loop finalize"
37             ) if defined $fileno_watcher[$fd]->[$mode];
38             }
39             }
40             }
41              
42              
43             # Maintain time watchers.
44             sub loop_resume_time_watcher {
45             my ($self, $next_time) = @_;
46             my $now = time;
47              
48             my $next = $next_time - $now;
49             $next *= 1000;
50             $next = 0 if $next < 0;
51              
52             if (defined $_watcher_timer) {
53             Glib::Source->remove($_watcher_timer);
54             }
55             $_watcher_timer = Glib::Timeout->add($next, \&_loop_event_callback);
56             }
57              
58             # we remove the old Glib::Timeout anyway, so resume amounts to
59             # the same thing as reset.
60             *loop_reset_time_watcher = \*loop_resume_time_watcher;
61              
62             sub _loop_resume_timer {
63             Glib::Source->remove($_idle_timer);
64             $_idle_timer = undef;
65             $poe_kernel->loop_resume_time_watcher($poe_kernel->get_next_event_time());
66             }
67              
68             sub loop_pause_time_watcher {
69             # does nothing
70             }
71              
72              
73             # Maintain filehandle watchers.
74             sub loop_watch_filehandle {
75             my ($self, $handle, $mode) = @_;
76             my $fileno = fileno($handle);
77              
78             # Overwriting a pre-existing watcher?
79             if (defined $fileno_watcher[$fileno]->[$mode]) {
80             Glib::Source->remove($fileno_watcher[$fileno]->[$mode]);
81             undef $fileno_watcher[$fileno]->[$mode];
82             }
83              
84             if (TRACE_FILES) {
85             POE::Kernel::_warn " watching $handle in mode $mode";
86             }
87              
88             # Register the new watcher.
89             $fileno_watcher[$fileno]->[$mode] =
90             Glib::IO->add_watch( $fileno,
91             ( ($mode == MODE_RD)
92             ? ( ['G_IO_IN', 'G_IO_HUP', 'G_IO_ERR'],
93             \&_loop_select_read_callback
94             )
95             : ( ($mode == MODE_WR)
96             ? ( ['G_IO_OUT', 'G_IO_ERR'],
97             \&_loop_select_write_callback
98             )
99             : ( 'G_IO_HUP',
100             \&_loop_select_expedite_callback
101             )
102             )
103             ),
104             );
105             }
106              
107             sub loop_ignore_filehandle {
108             my ($self, $handle, $mode) = @_;
109             my $fileno = fileno($handle);
110              
111             if (TRACE_FILES) {
112             POE::Kernel::_warn " ignoring $handle in mode $mode";
113             }
114              
115             # Don't bother removing a select if none was registered.
116             if (defined $fileno_watcher[$fileno]->[$mode]) {
117             Glib::Source->remove($fileno_watcher[$fileno]->[$mode]);
118             undef $fileno_watcher[$fileno]->[$mode];
119             }
120             }
121              
122             sub loop_pause_filehandle {
123             my ($self, $handle, $mode) = @_;
124             my $fileno = fileno($handle);
125              
126             if (TRACE_FILES) {
127             POE::Kernel::_warn " pausing $handle in mode $mode";
128             }
129              
130             Glib::Source->remove($fileno_watcher[$fileno]->[$mode]);
131             undef $fileno_watcher[$fileno]->[$mode];
132             }
133              
134             sub loop_resume_filehandle {
135             my ($self, $handle, $mode) = @_;
136             my $fileno = fileno($handle);
137              
138             # Quietly ignore requests to resume unpaused handles.
139             return 1 if defined $fileno_watcher[$fileno]->[$mode];
140              
141             if (TRACE_FILES) {
142             POE::Kernel::_warn " resuming $handle in mode $mode";
143             }
144              
145             $fileno_watcher[$fileno]->[$mode] =
146             Glib::IO->add_watch( $fileno,
147             ( ($mode == MODE_RD)
148             ? ( ['G_IO_IN', 'G_IO_HUP', 'G_IO_ERR'],
149             \&_loop_select_read_callback
150             )
151             : ( ($mode == MODE_WR)
152             ? ( ['G_IO_OUT', 'G_IO_ERR'],
153             \&_loop_select_write_callback
154             )
155             : ( 'G_IO_HUP',
156             \&_loop_select_expedite_callback
157             )
158             )
159             ),
160             );
161             return 1;
162             }
163              
164              
165             # Callbacks.
166              
167             # Event callback to dispatch pending events.
168             my $last_time = time();
169              
170             sub _loop_event_callback {
171             my $self = $poe_kernel;
172              
173             if (TRACE_STATISTICS) {
174             # TODO - I'm pretty sure the startup time will count as an unfair
175             # amout of idleness.
176             #
177             # TODO - Introducing many new time() syscalls. Bleah.
178             $self->_data_stat_add('idle_seconds', time() - $last_time);
179             }
180              
181             $self->_data_ev_dispatch_due();
182             $self->_test_if_kernel_is_idle();
183              
184             if (defined $_idle_timer) {
185             Glib::Source->remove ($_idle_timer);
186             $_idle_timer = undef;
187             }
188             if ($self->get_event_count()) {
189             $_idle_timer = Glib::Idle->add(\&_loop_resume_timer);
190             }
191              
192             $last_time = time() if TRACE_STATISTICS;
193              
194             # Return false to stop.
195             return 0;
196             }
197              
198             # Filehandle callback to dispatch selects.
199             sub _loop_select_read_callback {
200             my $self = $poe_kernel;
201             my ($fileno, $tag) = @_;
202              
203             if (TRACE_FILES) {
204             POE::Kernel::_warn " got read callback for $fileno";
205             }
206              
207             $self->_data_handle_enqueue_ready(MODE_RD, $fileno);
208             $self->_test_if_kernel_is_idle();
209              
210             return 1;
211             }
212              
213             sub _loop_select_write_callback {
214             my $self = $poe_kernel;
215             my ($fileno, $tag) = @_;
216              
217             if (TRACE_FILES) {
218             POE::Kernel::_warn " got write callback for $fileno";
219             }
220              
221             $self->_data_handle_enqueue_ready(MODE_WR, $fileno);
222             $self->_test_if_kernel_is_idle();
223              
224             return 1;
225             }
226              
227              
228             # The event loop itself.
229             sub loop_do_timeslice {
230             die "doing timeslices currently not supported in the Glib loop";
231             }
232              
233             my $glib_mainloop;
234              
235             #------------------------------------------------------------------------------
236             # Loop construction and destruction.
237              
238             sub loop_attach_uidestroy {
239             my ($self, $window) = @_;
240              
241             # Don't bother posting the signal if there are no sessions left. I
242             # think this is a bit of a kludge: the situation where a window
243             # lasts longer than POE::Kernel should never occur.
244             $window->signal_connect
245             ( delete_event =>
246             sub {
247             if ($self->_data_ses_count()) {
248             $self->_dispatch_event(
249             $self, $self,
250             EN_SIGNAL, ET_SIGNAL, [ 'UIDESTROY' ],
251             __FILE__, __LINE__, time(), -__LINE__
252             );
253             }
254             return 0;
255             }
256             );
257             }
258              
259             sub loop_initialize {
260             my $self = shift;
261              
262             $glib_mainloop = Glib::MainLoop->new unless (Glib::main_depth() > 0);
263             Glib->install_exception_handler (\&ex);
264              
265             }
266              
267             sub loop_run {
268             my $self = shift;
269              
270             # fixes RT#49742, thanks dngor for tracking it down!
271             if ( $self->_data_ses_count() ) {
272             $self->_test_if_kernel_is_idle();
273             (defined $glib_mainloop) && $glib_mainloop->run;
274             if (defined $POE::Kernel::_glib_loop_exception) {
275             my $ex = $POE::Kernel::_glib_loop_exception;
276             undef $POE::Kernel::_glib_loop_exception;
277             die $ex;
278             }
279             }
280             }
281              
282             sub loop_halt {
283             (defined $glib_mainloop) && $glib_mainloop->quit;
284             }
285              
286             our $_glib_loop_exception;
287              
288             sub ex {
289             $_glib_loop_exception = shift;
290             &loop_finalize;
291             &loop_halt;
292              
293             return 0;
294             }
295              
296             1;
297             __END__