ebae792c89ca248a945500e62c70dba84a208237
[debian/amanda] / perl / Amanda / MainLoop.swg
1 /*
2  * Copyright (c) 2008 Zmanda, Inc.  All Rights Reserved.
3  *
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.
7  *
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.
12  *
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.
16  *
17  * Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300
18  * Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
19  */
20
21 %module "Amanda::MainLoop"
22 %include "amglue/amglue.swg"
23 %include "exception.i"
24
25 %{
26 #include "amanda.h"
27 #include "event.h"
28 %}
29
30 %perlcode %{
31 =head1 NAME
32
33 Amanda::MainLoop - Perl interface to the Glib MainLoop
34
35 =head1 SYNOPSIS
36
37     use Amanda::MainLoop;
38     
39     my $to = Amanda::MainLoop::timeout_source(2000);
40     $to->set_callback(sub { 
41         print "Time's Up!\n";
42         $to->remove();              # dont' re-queue this timeout
43         Amanda::MainLoop::quit();   # return from Amanda::MainLoop::run
44     });
45
46     Amanda::MainLoop::run();
47
48 Note that all functions in this module are individually available for export,
49 e.g.,
50
51     use Amanda::MainLoop qw(run quit);
52
53 =head1 OVERVIEW
54
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.
59
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.
62
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.
67
68 =head2 LOOPING
69
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()>.
74
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:
79
80     my $cb = sub {
81         my ($letters, $digits) = @_;
82         # ..
83         Amanda::MainLoop::quit();
84     };
85     Amanda::MainLoop::call_later($cb, "abc", 123);
86     # ..
87     Amanda::MainLoop::run();
88
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
92 MainLoop:
93
94     sub might_delay {
95         my ($cb) = @_;
96         if (can_do_it_now()) {
97             my $result = do_it();
98             Amanda::MainLoop::call_later($cb, $result)
99         } else {
100             # ..
101         }
102     }
103
104 =head2 EVENT SOURCES
105
106 An event source is responsible for producing events.  A source may produce
107 multiple events over its lifetime.
108
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.
114
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
118 example:
119
120   sub start_timer { 
121     my ($loops) = @_;
122
123     Amanda::MainLoop::timeout_source(200)->set_callback(sub {
124       my ($src) = @_;
125       print "timer\n";
126       if (--$loops <= 0) {
127         $src->remove();
128         Amanda::MainLoop::quit();
129       }
130     });
131   }
132   start_timer(10);
133   Amanda::MainLoop::run();
134
135 =head3 Timeout
136
137   my $src = Amanda::MainLoop::timeout_source(10000);
138
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
141 source is destroyed.
142
143 =head3 Idle
144
145   my $src = Amanda::MainLoop::idle_source(2);
146
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.
151
152 =head3 Child Watch
153
154   my $src = Amanda::MainLoop::child_watch_source($pid);
155
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.
160
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
165 possible situations.
166
167 =head3 File Descriptor
168
169   my $src = Amanda::MainLoop::fd_source($fd, G_IO_IN);
170
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
175 C<:GIOCondition>.
176
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>.
180
181 =head1 RELATION TO GLIB
182
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:
189
190 =over
191
192 =item Amanda's perl-accessible main loop only runs a single C<GMainContext>, and
193 always runs in the main thread.
194
195 =item Besides idle sources, event priorities are not accessible from Perl.
196
197 =back
198
199 =head1 PROGRAMMING HINTS
200
201 Most often, callbacks are short, and can be specified as anonymous subs:
202
203     $src->set_callback(sub {
204         my ($src) = @_;
205         # ...
206     });
207
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
211 anonymous) sub:
212
213     {
214         my $total_results = 0;
215
216         $src->set_callback(sub {
217             my ($src, $result) = @_;
218             $total_results += $result;
219         });
220     }
221
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:
226
227     {
228         my $op_src = long_operation_src();
229         my $timeout_src = Amanda::MainLoop::timeout_source($timeout);
230
231         sub finish {
232             $op_src->remove();
233             $timeout_src->remove();
234         }
235
236         $op_src->set_callback(sub {
237             print "Operation complete\n";
238             finish();
239         });
240
241         $timeout_src->set_callback(sub {
242             print "Operation timed out\n";
243             finish();
244         });
245     }
246
247 Both of these limitations may be lifted in future revisions of
248 L<Amanda::MainLoop>.
249
250 =cut
251 %}
252
253 /*
254  * Looping
255  */
256
257 %perlcode %{
258 # glib's g_is_main_loop_running() seems inaccurate, so we just
259 # track that information locally..
260 my $mainloop_running = 0;
261
262 my @waiting_to_call_later;
263 sub call_later {
264     my ($sub, @args) = @_;
265
266     # add the callback if nothing is waiting right now
267     if (!@waiting_to_call_later) {
268         timeout_source(0)->set_callback(sub {
269             my ($src) = @_;
270             $src->remove();
271
272             while (@waiting_to_call_later) {
273                 my ($sub, @args) = @{pop @waiting_to_call_later};
274                 $sub->(@args);
275             }
276         });
277     }
278
279     push @waiting_to_call_later, [ $sub, @args ];
280 }
281
282 sub run {
283     $mainloop_running = 1;
284     run_c();
285     $mainloop_running = 0;
286 }
287
288 sub is_running {
289     return $mainloop_running;
290 }
291 %}
292
293 %inline %{
294 void run_c(void) {
295     g_main_loop_run(default_main_loop());
296 }
297
298 void quit(void) {
299     g_main_loop_quit(default_main_loop());
300 }
301 %}
302
303 /*
304  * Event Sources
305  */
306
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.
311  */
312
313 %{ static void amglue_source_remove(amglue_Source *self); %}
314
315 %rename(Source) amglue_Source;
316 typedef struct amglue_Source {
317     %extend {
318         /* Constructor: use one of the package-level functions, below */
319         amglue_Source() {
320             die("Amanda::MainLoop::Source is an abstract base class");
321         }
322
323         /* Destructor: just unref the object */
324         ~amglue_Source() {
325             amglue_source_unref(self);
326         }
327
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.
334              */
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;
339
340                 g_source_attach(self->src, NULL);
341
342                 /* the link from the GSource to the amglue_Source is
343                  * now in use, so we increment the amglue_Source's 
344                  * refcount. */
345                 amglue_source_ref(self);
346             }
347
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
354              * modifies it. */
355             if (self->callback_sv) {
356                 SvSetSV(self->callback_sv, callback_sub);
357             } else {
358                 self->callback_sv = newSVsv(callback_sub);
359                 g_source_set_callback(self->src, self->callback,
360                     (gpointer)self, NULL);
361             }
362         }
363         /* delete the cheater's typemap */
364         %typemap(in) SV *sv;
365
366         void remove(void) {
367             amglue_source_remove(self);
368         }
369     }
370 } amglue_Source;
371
372 %{
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 */
375 static void
376 amglue_source_remove(
377     amglue_Source *self)
378 {
379     /* protect against self being freed out from under us */
380     amglue_source_ref(self);
381
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;
387         }
388
389         /* undo the ref made in set_callback() */
390         amglue_source_unref(self);
391
392         g_source_destroy(self->src);
393     }
394
395     self->state = AMGLUE_SOURCE_DESTROYED;
396
397     /* reverse the "protection" increment used above */
398     amglue_source_unref(self);
399 }
400 %}
401
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. */
406 %{
407 static gboolean
408 amglue_source_callback_simple(
409     gpointer *data)
410 {
411     dSP;
412     amglue_Source *src = (amglue_Source *)data;
413     SV *src_sv = NULL;
414
415     g_assert(src->callback_sv != NULL);
416
417     ENTER;
418     SAVETMPS;
419
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);
426
427     PUSHMARK(SP);
428     XPUSHs(src_sv);
429     PUTBACK;
430
431     call_sv(src->callback_sv, G_EVAL|G_DISCARD);
432
433     FREETMPS;
434     LEAVE;
435
436     /* these may have been freed, so don't use them after this point */
437     src_sv = NULL;
438     src = NULL;
439
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)
442      * state. */
443     if (SvTRUE(ERRSV)) {
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));
448         exit(1);
449     }
450
451     return TRUE;
452 }
453 %}
454
455 /* Constructors for some general-purpose sources */
456
457 /* timeout source */
458 %newobject timeout_source;
459 %inline %{
460 amglue_Source *
461 timeout_source(
462     guint interval)
463 {
464     return amglue_source_new(g_timeout_source_new(interval), 
465         (GSourceFunc)amglue_source_callback_simple);
466 }
467 %}
468
469 /* idle source */
470 %newobject idle_source;
471 %inline %{
472 amglue_Source *
473 idle_source(
474     gint priority)
475 {
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);
480 }
481 %}
482
483 /* child watch source */
484 %{
485 static gboolean
486 child_watch_source_callback(
487     pid_t pid,
488     gint status,
489     gpointer data)
490 {
491     dSP;
492     amglue_Source *src = (amglue_Source *)data;
493     SV *src_sv;
494
495     g_assert(src->callback_sv != NULL);
496
497     ENTER;
498     SAVETMPS;
499
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);
506
507     PUSHMARK(SP);
508     XPUSHs(src_sv);
509     XPUSHs(sv_2mortal(newSViv(pid)));
510     XPUSHs(sv_2mortal(newSViv(status)));
511     PUTBACK;
512
513     call_sv(src->callback_sv, G_EVAL|G_DISCARD);
514
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);
518
519     FREETMPS;
520     LEAVE;
521
522     /* these may have been freed, so don't use them after this point */
523     src_sv = NULL;
524     src = NULL;
525
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)
528      * state. */
529     if (SvTRUE(ERRSV)) {
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));
534         exit(1);
535     }
536
537     return TRUE;
538 }
539 %}
540 %newobject child_watch_source;
541 %inline %{
542 amglue_Source *
543 child_watch_source(
544     gint pid)
545 {
546     GSource *child_watch_source = new_child_watch_source(pid);
547     return amglue_source_new(child_watch_source,
548         (GSourceFunc)child_watch_source_callback);
549 }
550 %}
551
552 /* fd source */
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);
562
563 %newobject fd_source;
564 %inline %{
565 amglue_Source *
566 fd_source(
567     gint fd,
568     GIOCondition events)
569 {
570     GSource *fdsource = new_fdsource(fd, events);
571     return amglue_source_new(fdsource,
572         (GSourceFunc)amglue_source_callback_simple);
573 }
574 %}