… | |
… | |
17 | |
17 | |
18 | Glib is probably the most inefficient event loop that has ever seen the |
18 | Glib is probably the most inefficient event loop that has ever seen the |
19 | light of the world: Glib not only scans all its watchers (really, ALL of |
19 | light of the world: Glib not only scans all its watchers (really, ALL of |
20 | them, whether I/O-related, timer-related or what not) during each loop |
20 | them, whether I/O-related, timer-related or what not) during each loop |
21 | iteration, it also does so multiple times and rebuilds the poll list for |
21 | iteration, it also does so multiple times and rebuilds the poll list for |
22 | the kernel each time again, dynamically even. |
22 | the kernel each time again, dynamically even. Newer versions of libglib |
|
|
23 | fortunately do not call malloc/free on every single watcher invocation, |
|
|
24 | though. |
23 | |
25 | |
|
|
26 | Glib also enforces certain undocumented behaviours, for example, you |
|
|
27 | cannot always remove active child watchers, and the conditions on when |
|
|
28 | it is valid to do so are not documented. Of course, if you get it wrong, |
|
|
29 | you get "GLib-CRITICAL" messages. This makes it extremely hard to write |
|
|
30 | "correct" glib programs, as you have to study the source code to get it |
|
|
31 | right, and hope future versions don't change any internals. |
|
|
32 | |
|
|
33 | AnyEvent implements the necessary workarounds, at a small performance |
|
|
34 | cost. |
|
|
35 | |
24 | On the positive side, and most importantly, Glib generally works |
36 | On the positive side, and most importantly, when it works, Glib generally |
25 | correctly, no quarrels there. |
37 | works correctly, no quarrels there. |
26 | |
38 | |
27 | If you create many watchers (as in: more than two), you might consider one |
39 | If you create many watchers (as in: more than two), you might consider one |
28 | of the L<Glib::EV>, L<EV::Glib> or L<Glib::Event> modules that map Glib to |
40 | of the L<Glib::EV>, L<EV::Glib> or L<Glib::Event> modules that map Glib to |
29 | other, more efficient, event loops. |
41 | other, more efficient, event loops. |
30 | |
42 | |
… | |
… | |
32 | |
44 | |
33 | =cut |
45 | =cut |
34 | |
46 | |
35 | package AnyEvent::Impl::Glib; |
47 | package AnyEvent::Impl::Glib; |
36 | |
48 | |
37 | no warnings; |
49 | use AnyEvent (); BEGIN { AnyEvent::common_sense } |
38 | use strict; |
50 | use Glib 1.210 (); # (stable 1.220 2009, also Glib 2.4+ required, 2004) |
39 | |
|
|
40 | use Glib (); |
|
|
41 | |
51 | |
42 | our $mainloop = Glib::MainContext->default; |
52 | our $mainloop = Glib::MainContext->default; |
|
|
53 | |
|
|
54 | my %io_cond = ( |
|
|
55 | r => ["in" , "hup"], |
|
|
56 | w => ["out", "hup"], |
|
|
57 | ); |
43 | |
58 | |
44 | sub io { |
59 | sub io { |
45 | my ($class, %arg) = @_; |
60 | my ($class, %arg) = @_; |
46 | |
61 | |
47 | my $cb = $arg{cb}; |
62 | my $cb = $arg{cb}; |
|
|
63 | my $fd = fileno $arg{fh}; |
|
|
64 | defined $fd or $fd = $arg{fh}; |
48 | |
65 | |
49 | my @cond; |
66 | my $source = add_watch Glib::IO |
50 | # some glibs need hup, others error with it, YMMV |
67 | $fd, |
51 | push @cond, "in", "hup" if $arg{poll} eq "r"; |
68 | $io_cond{$arg{poll}}, |
52 | push @cond, "out", "hup" if $arg{poll} eq "w"; |
69 | sub { &$cb; 1 }; |
53 | |
70 | |
54 | my $source = add_watch Glib::IO fileno $arg{fh}, \@cond, sub { &$cb; 1 }; |
|
|
55 | bless \\$source, $class |
71 | bless \\$source, $class |
56 | } |
72 | } |
57 | |
73 | |
58 | sub timer { |
74 | sub timer { |
59 | my ($class, %arg) = @_; |
75 | my ($class, %arg) = @_; |
60 | |
76 | |
61 | my $cb = $arg{cb}; |
77 | my $cb = $arg{cb}; |
62 | my $ival = $arg{interval} * 1000; |
78 | my $ival = $arg{interval} * 1000; |
63 | |
79 | |
64 | my $source; $source = add Glib::Timeout $arg{after} * 1000, |
80 | my $source; $source = add Glib::Timeout $arg{after} < 0 ? 0 : $arg{after} * 1000, |
65 | $ival ? sub { |
81 | $ival ? sub { |
66 | remove Glib::Source $source; |
82 | remove Glib::Source $source; |
67 | $source = add Glib::Timeout $ival, sub { &$cb; 1 }; |
83 | $source = add Glib::Timeout $ival, sub { &$cb; 1 }; |
68 | &$cb; |
84 | &$cb; |
69 | 0 |
85 | 1 # already removed, should be a nop |
70 | } |
86 | } |
71 | : sub { &$cb; 0 }; |
87 | : sub { |
|
|
88 | # due to the braindamaged libglib API (it manages |
|
|
89 | # removed-but-active watchers internally, but forces |
|
|
90 | # users to # manage the same externally as well), |
|
|
91 | # we have to go through these contortions. |
|
|
92 | remove Glib::Source $source; |
|
|
93 | undef $source; |
|
|
94 | &$cb; |
|
|
95 | 1 # already removed, should be a nop |
|
|
96 | }; |
72 | |
97 | |
73 | bless \\$source, $class |
98 | bless \\$source, $class |
74 | } |
99 | } |
75 | |
100 | |
76 | sub idle { |
101 | sub idle { |
77 | my ($class, %arg) = @_; |
102 | my ($class, %arg) = @_; |
78 | |
103 | |
79 | my $cb = $arg{cb}; |
104 | my $cb = $arg{cb}; |
80 | my $source = add Glib::Idle sub { &$cb; 1 }; |
105 | my $source = add Glib::Idle sub { &$cb; 1 }; |
|
|
106 | |
81 | bless \\$source, $class |
107 | bless \\$source, $class |
82 | } |
108 | } |
83 | |
109 | |
84 | sub DESTROY { |
110 | sub DESTROY { |
85 | remove Glib::Source $${$_[0]}; |
111 | remove Glib::Source $${$_[0]} |
|
|
112 | if defined $${$_[0]}; |
86 | } |
113 | } |
87 | |
114 | |
88 | sub one_event { |
115 | our %pid_w; |
|
|
116 | our %pid_cb; |
|
|
117 | |
|
|
118 | sub child { |
|
|
119 | my ($class, %arg) = @_; |
|
|
120 | |
|
|
121 | $arg{pid} > 0 |
|
|
122 | or Carp::croak "Glib does not support watching for all pids (pid == 0) as attempted"; |
|
|
123 | |
|
|
124 | my $pid = $arg{pid}; |
|
|
125 | my $cb = $arg{cb}; |
|
|
126 | |
|
|
127 | $pid_cb{$pid}{$cb+0} = $cb; |
|
|
128 | |
|
|
129 | $pid_w{$pid} ||= Glib::Child->watch_add ($pid, sub { |
|
|
130 | # the unbelievably braindamaged glib api ignores the return |
|
|
131 | # value and always removes the watcher (this is of course |
|
|
132 | # undocumented), so we need to go through these contortions to |
|
|
133 | # work around this, here and in DESTROY. |
|
|
134 | undef $pid_w{$pid}; |
|
|
135 | |
|
|
136 | $_->($_[0], $_[1]) |
|
|
137 | for values %{ $pid_cb{$pid} }; |
|
|
138 | |
|
|
139 | 1 # gets ignored |
|
|
140 | }); |
|
|
141 | |
|
|
142 | bless [$pid, $cb+0], "AnyEvent::Impl::Glib::child" |
|
|
143 | } |
|
|
144 | |
|
|
145 | sub AnyEvent::Impl::Glib::child::DESTROY { |
|
|
146 | my ($pid, $icb) = @{ $_[0] }; |
|
|
147 | |
|
|
148 | delete $pid_cb{$pid}{$icb}; |
|
|
149 | unless (%{ $pid_cb{$pid} }) { |
|
|
150 | delete $pid_cb{$pid}; |
|
|
151 | my $source = delete $pid_w{$pid}; |
|
|
152 | remove Glib::Source if defined $source; |
|
|
153 | } |
|
|
154 | } |
|
|
155 | |
|
|
156 | #sub loop { |
|
|
157 | # # hackish, but we do not have a mainloop, just a maincontext |
|
|
158 | # $mainloop->iteration (1) while 1; |
|
|
159 | #} |
|
|
160 | |
|
|
161 | sub _poll { |
89 | $mainloop->iteration (1); |
162 | $mainloop->iteration (1); |
90 | } |
163 | } |
91 | |
164 | |
92 | sub loop { |
165 | sub AnyEvent::CondVar::Base::_wait { |
93 | $mainloop->iteration (1) while 1; # hackish, but w ehave no mainloop |
166 | $mainloop->iteration (1) until exists $_[0]{_ae_sent}; |
94 | } |
167 | } |
95 | |
|
|
96 | 1; |
|
|
97 | |
168 | |
98 | =head1 SEE ALSO |
169 | =head1 SEE ALSO |
99 | |
170 | |
100 | L<AnyEvent>, L<Glib>. |
171 | L<AnyEvent>, L<Glib>. |
101 | |
172 | |
102 | =head1 AUTHOR |
173 | =head1 AUTHOR |
103 | |
174 | |
104 | Marc Lehmann <schmorp@schmorp.de> |
175 | Marc Lehmann <schmorp@schmorp.de> |
105 | http://home.schmorp.de/ |
176 | http://anyevent.schmorp.de |
106 | |
177 | |
107 | =cut |
178 | =cut |
108 | |
179 | |
|
|
180 | 1 |
|
|
181 | |