2 * Copyright (c) 2008 Zmanda, Inc. All Rights Reserved.
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
11 * License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17 * Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300
18 * Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
21 %module "Amanda::MainLoop"
22 %include "amglue/amglue.swg"
23 %include "exception.i"
33 Amanda::MainLoop - Perl interface to the Glib MainLoop
39 my $to = Amanda::MainLoop::timeout_source(2000);
40 $to->set_callback(sub {
42 $to->remove(); # dont' re-queue this timeout
43 Amanda::MainLoop::quit(); # return from Amanda::MainLoop::run
46 Amanda::MainLoop::run();
48 Note that all functions in this module are individually available for export,
51 use Amanda::MainLoop qw(run quit);
55 The main event loop of an application is a tight loop which waits for events,
56 and calls functions to respond to those events. This design allows an IO-bound
57 application to multitask within a single thread, by responding to IO events as
58 they occur instead of blocking on particular IO operations.
60 The Amanda security API, transfer API, and other components rely on the event
61 loop to allow them to respond to their own events in a timely fashion.
63 The overall structure of an application, then, is to initialize its state,
64 register callbacks for some events, and begin looping. When some
65 application-defined state is reached, the loop is terminated and the
66 application cleans up and exits.
70 The actual event loop takes place within a call to C<Amanda::MainLoop::run()>.
71 This function executes until a call to C<Amanda::MainLoop::quit()> occurs, at
72 which point C<run()> returns. You can check whether the loop is running with
73 C<Amanda::MainLoop::is_running()>.
75 In some cases, a sub should only run after the loop has started. The
76 most common case is when a callback may call C<Amanda::MainLoop::quit>
77 immediately. In this circumstance, use C<call_later>, which takes a
78 subref and an arbitrary number of arguments for that sub:
81 my ($letters, $digits) = @_;
83 Amanda::MainLoop::quit();
85 Amanda::MainLoop::call_later($cb, "abc", 123);
87 Amanda::MainLoop::run();
89 Similarly, a tight loop of callbacks with no blocking operations can
90 lead to excessive stack consumption. In this case, C<call_later()> is
91 useful, too. It queues the callback to run at the next cycle of the
96 if (can_do_it_now()) {
98 Amanda::MainLoop::call_later($cb, $result)
106 An event source is responsible for producing events. A source may produce
107 multiple events over its lifetime.
109 The method C<$src->set_callback(\&cb)> sets the function that will be called
110 for a given source, and "attaches" the source to the main loop so that it will
111 begin generating events. The arguments to the callback depend on the event
112 source, but the first argument is always the source itself. Unless specified,
113 no other arguments are provided.
115 Event sources persist until they are removed with C<$src->remove()>, even if
116 the source itself is no longer accessible from Perl. Although Glib supports
117 it, there is no provision for "automatically" removing an event source. As an
123 Amanda::MainLoop::timeout_source(200)->set_callback(sub {
128 Amanda::MainLoop::quit();
133 Amanda::MainLoop::run();
137 my $src = Amanda::MainLoop::timeout_source(10000);
139 A timeout source will create events at the specified interval, specified in
140 milliseconds (thousandths of a second). The events will continue until the
145 my $src = Amanda::MainLoop::idle_source(2);
147 An idle source will create events continuously except when a higher-priority
148 source is emitting events. Priorities are generally small positive integers,
149 with larger integers denoting lower priorities. The events will continue until
150 the source is destroyed.
154 my $src = Amanda::MainLoop::child_watch_source($pid);
156 A child watch source will issue an event when the process with the given PID
157 dies. To avoid race conditions, it will issue an event even if the process
158 dies before the source is created. The callback is called with three
159 arguments: the event source, the PID, and the child's exit status.
161 Note that this source is totally incompatible with any thing that would cause
162 perl to change the SIGCHLD handler. If SIGCHLD is changed, under some
163 circumstances the module will recognize this circumstance, add a warning to the
164 debug log, and continue operating. However, it is impossible to catch all
167 =head3 File Descriptor
169 my $src = Amanda::MainLoop::fd_source($fd, G_IO_IN);
171 This source will issuen an event whenever one of the given conditions is true
172 for the given file descriptor. The conditions are from Glib's GIOCondition,
173 and are C<$G_IO_IN>, C<G_IO_OUT>, C<$G_IO_PRI>, C<$G_IO_ERR>, C<$G_IO_HUP>, and
174 C<$G_IO_NVAL>. These constants are available with the import tag
177 Generally, when reading from a file descriptor, use C<$G_IO_IN|$G_IO_HUP> to
178 ensure that an EOF triggers an event as well. Writing to a file descriptor can
179 simply use C<$G_IO_OUT>.
181 =head1 RELATION TO GLIB
183 Glib's main event loop is described in the Glib manual:
184 L<http://library.gnome.org/devel/glib/stable/glib-The-Main-Event-Loop.html>.
185 Note that Amanda depends only on the functionality available in Glib-2.2.0, so
186 many functions described in that document are not available in Amanda. This
187 module provides a much-simplified interface to the glib library, and is not
188 intended as a generic wrapper for it. Specifically:
192 =item Amanda's perl-accessible main loop only runs a single C<GMainContext>, and
193 always runs in the main thread.
195 =item Besides idle sources, event priorities are not accessible from Perl.
199 =head1 PROGRAMMING HINTS
201 Most often, callbacks are short, and can be specified as anonymous subs:
203 $src->set_callback(sub {
208 There is currently no means in place to specify extra arguments for a callback
209 when it is set. If the callback needs access to other data, it should use a
210 Perl closure in the form of lexically scoped variables and a (possibly
214 my $total_results = 0;
216 $src->set_callback(sub {
217 my ($src, $result) = @_;
218 $total_results += $result;
222 Event sources are often set up in groups, e.g., a long-term operation and a
223 timeout. When this is the case, be careful that all sources are removed when
224 the operation is complete. The easiest way to accomplish this is to include all
225 sources in a lexical scope and remove them at the appropriate times:
228 my $op_src = long_operation_src();
229 my $timeout_src = Amanda::MainLoop::timeout_source($timeout);
233 $timeout_src->remove();
236 $op_src->set_callback(sub {
237 print "Operation complete\n";
241 $timeout_src->set_callback(sub {
242 print "Operation timed out\n";
247 Both of these limitations may be lifted in future revisions of
258 # glib's g_is_main_loop_running() seems inaccurate, so we just
259 # track that information locally..
260 my $mainloop_running = 0;
262 my @waiting_to_call_later;
264 my ($sub, @args) = @_;
266 # add the callback if nothing is waiting right now
267 if (!@waiting_to_call_later) {
268 timeout_source(0)->set_callback(sub {
272 while (@waiting_to_call_later) {
273 my ($sub, @args) = @{pop @waiting_to_call_later};
279 push @waiting_to_call_later, [ $sub, @args ];
283 $mainloop_running = 1;
285 $mainloop_running = 0;
289 return $mainloop_running;
295 g_main_loop_run(default_main_loop());
299 g_main_loop_quit(default_main_loop());
307 /* First we wrap the amglue_Source struct, defined in
308 * perl/amglue/mainloop.h, into a Perl object (named
309 * Amanda::MainLoop::Source). After that appear several
310 * constructors for various event sources.
313 %{ static void amglue_source_remove(amglue_Source *self); %}
315 %rename(Source) amglue_Source;
316 typedef struct amglue_Source {
318 /* Constructor: use one of the package-level functions, below */
320 die("Amanda::MainLoop::Source is an abstract base class");
323 /* Destructor: just unref the object */
325 amglue_source_unref(self);
328 /* a "cheater's typemap" to just pass the SV along */
329 %typemap(in) SV *callback_sub "$1 = $input;"
330 void set_callback(SV *callback_sub) {
331 /* Attach the source to the default mainloop context, so
332 * that it will start generating events. If it's already
333 * been destroyed, then bail with a fatal error.
335 if (self->state == AMGLUE_SOURCE_DESTROYED) {
336 die("This source has already been removed");
337 } else if (self->state == AMGLUE_SOURCE_NEW) {
338 self->state = AMGLUE_SOURCE_ATTACHED;
340 g_source_attach(self->src, NULL);
342 /* the link from the GSource to the amglue_Source is
343 * now in use, so we increment the amglue_Source's
345 amglue_source_ref(self);
348 /* whoever created this Source object conveniently left
349 * the proper C-side callback for us. This function has
350 * the appropriate calling signature for this GSource, and
351 * knows how to reflect that into Perl. It expects the SV to
352 * be provided as its 'data' argument. 'perlcall' suggests
353 * that we make a copy of this SV, in case the user later
355 if (self->callback_sv) {
356 SvSetSV(self->callback_sv, callback_sub);
358 self->callback_sv = newSVsv(callback_sub);
359 g_source_set_callback(self->src, self->callback,
360 (gpointer)self, NULL);
363 /* delete the cheater's typemap */
367 amglue_source_remove(self);
373 /* Detach a source from the mainloop and remove it from play. This is broken
374 * out as a separate function because it's also used from some callbacks */
376 amglue_source_remove(
379 /* protect against self being freed out from under us */
380 amglue_source_ref(self);
382 if (self->state == AMGLUE_SOURCE_ATTACHED) {
383 /* unref any perl callback */
384 if (self->callback_sv) {
385 SvREFCNT_dec(self->callback_sv);
386 self->callback_sv = NULL;
389 /* undo the ref made in set_callback() */
390 amglue_source_unref(self);
392 g_source_destroy(self->src);
395 self->state = AMGLUE_SOURCE_DESTROYED;
397 /* reverse the "protection" increment used above */
398 amglue_source_unref(self);
402 /* "Generic" callback function for a GSource that actually uses the GSourceFunc
403 * prototype. The source of this function also serves as a prototype for other,
404 * more advanced callbacks. Due to perl's heavy use of precompiler macros, it's
405 * not possible to break this down any further. */
408 amglue_source_callback_simple(
412 amglue_Source *src = (amglue_Source *)data;
415 g_assert(src->callback_sv != NULL);
420 /* create a new SV pointing to 'src', and increase our refcount
421 * accordingly. The SV is mortal, so FREETMPS will decrease the
422 * refcount, unless the callee keeps a copy of it somewhere */
423 amglue_source_ref(src);
424 src_sv = SWIG_NewPointerObj(src, SWIGTYPE_p_amglue_Source,
425 SWIG_OWNER | SWIG_SHADOW);
431 call_sv(src->callback_sv, G_EVAL|G_DISCARD);
436 /* these may have been freed, so don't use them after this point */
440 /* check for an uncaught 'die'. If we don't do this, then Perl will longjmp()
441 * over the GMainLoop mechanics, leaving GMainLoop in an inconsistent (locked)
444 /* We handle this just the way the default 'die' handler in Amanda::Debug
445 * does, but since Amanda's debug support may not yet be running, we back
446 * it up with an exit() */
447 g_critical("%s", SvPV_nolen(ERRSV));
455 /* Constructors for some general-purpose sources */
458 %newobject timeout_source;
464 return amglue_source_new(g_timeout_source_new(interval),
465 (GSourceFunc)amglue_source_callback_simple);
470 %newobject idle_source;
476 GSource *idle_source = g_idle_source_new();
477 g_source_set_priority(idle_source, priority);
478 return amglue_source_new(idle_source,
479 (GSourceFunc)amglue_source_callback_simple);
483 /* child watch source */
486 child_watch_source_callback(
492 amglue_Source *src = (amglue_Source *)data;
495 g_assert(src->callback_sv != NULL);
500 /* create a new SV pointing to 'src', and increase our refcount
501 * accordingly. The SV is mortal, so FREETMPS will decrease the
502 * refcount, unless the callee keeps a copy of it somewhere */
503 amglue_source_ref(src);
504 src_sv = SWIG_NewPointerObj(src, SWIGTYPE_p_amglue_Source,
505 SWIG_OWNER | SWIG_SHADOW);
509 XPUSHs(sv_2mortal(newSViv(pid)));
510 XPUSHs(sv_2mortal(newSViv(status)));
513 call_sv(src->callback_sv, G_EVAL|G_DISCARD);
515 /* child watch sources automatically destroy themselves after the
516 * child dies, so we mark the amglue_Source as destroyed, too. */
517 amglue_source_remove(src);
522 /* these may have been freed, so don't use them after this point */
526 /* check for an uncaught 'die'. If we don't do this, then Perl will longjmp()
527 * over the GMainLoop mechanics, leaving GMainLoop in an inconsistent (locked)
530 /* We handle this just the way the default 'die' handler in Amanda::Debug
531 * does, but since Amanda's debug support may not yet be running, we back
532 * it up with an exit() */
533 g_critical("%s", SvPV_nolen(ERRSV));
540 %newobject child_watch_source;
546 GSource *child_watch_source = new_child_watch_source(pid);
547 return amglue_source_new(child_watch_source,
548 (GSourceFunc)child_watch_source_callback);
553 %apply gint { GIOCondition };
554 amglue_add_flag_tag_fns(GIOCondition);
555 amglue_add_constant(G_IO_IN, GIOCondition);
556 amglue_add_constant(G_IO_OUT, GIOCondition);
557 amglue_add_constant(G_IO_PRI, GIOCondition);
558 amglue_add_constant(G_IO_ERR, GIOCondition);
559 amglue_add_constant(G_IO_HUP, GIOCondition);
560 amglue_add_constant(G_IO_NVAL, GIOCondition);
561 amglue_copy_to_tag(GIOCondition, constants);
563 %newobject fd_source;
570 GSource *fdsource = new_fdsource(fd, events);
571 return amglue_source_new(fdsource,
572 (GSourceFunc)amglue_source_callback_simple);