612dcac610bbbe550912de681cc249bf6b8c0283
[debian/amanda] / perl / amglue / source.c
1 /*
2  * Copyright (c) 2008,2009 Zmanda, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 as published
6  * by the Free Software Foundation.
7  *
8  * This program 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 General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16  *
17  * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18  * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
19  */
20
21 #include "amglue.h"
22
23 /* GSources are tricky to bind to perl for a few reasons:
24  *  - they have a one-way state machine: once attached and detached, they
25  *    cannot be re-attached
26  *  - different "kinds" of GSources require C-level callbacks with
27  *    different signatures
28  *  - an attached GSource should continue running, even if not referenced
29  *    from perl, while a detached GSource should free all its resources
30  *    when no longer referenced.
31  *
32  * To accomplish all of this, this file implements a "glue object" called
33  * amglue_Source.  There are zero or one amglue_Source objects for each
34  * GSource object, so they serve as a place to store "extra" data about a
35  * GSource.  In particular, they store:
36  *  - a pointer to a C callback function that can trigger a Perl callback
37  *  - a pointer to an SV representing the perl callback to run
38  *  - a reference count
39  * Any number of Perl SV's may reference the amglue_Source -- it tracks this
40  * via its reference count.
41  *
42  * Let's look at this arrangement as it follows a typical usage scenario.  The
43  * numbers in brackets are reference counts.
44  *
45  * -- my $src = Amanda::MainLoop::new_foo_source();
46  * GSrc[1] <----) amSrc[1] <---- $src[1] <--- perl-stack
47  *
48  * The lexical $src contains a reference to the amglue_Source object, which is
49  * referencing the underlying GSource object.  Pretty simple.  The amglue_Source
50  * only counts one reference because the GSource isn't yet attached.  Think of
51  * the ')' in the diagram as a weak reference.  If the perl scope were to end
52  * now, all of these objects would be freed immediately.
53  *
54  * -- $src->set_callback(\&cb);
55  *                              ,--> &cb[1]
56  * GMainLoop --> GSrc[2] <---> amSrc[2]
57  *                              ^--- $src[1] <--- perl-stack
58  *
59  * The GSource has been attached, so GMainLoop holds a reference to it.  The
60  * amglue_Source incremented its own reference count, making the previous weak
61  * reference a full reference, because the link from the GSource will be used
62  * when a callback occurs.  The amglue_Source object also keeps a reference to
63  * the callback coderef.
64  *
65  * -- return;
66  *                              ,--> &cb[1]
67  * GMainLoop --> GSrc[2] <---> amSrc[1]
68  *
69  * When the perl scope ends, the lexical $src is freed, reducing the reference
70  * count on the amglue_Source to 1.  At this point, the object is not accessible
71  * from perl, but it is still accessible from the GSource via a callback.
72  *
73  * -- # in callback
74  *                              ,--> &cb[1]
75  * GMainLoop --> GSrc[2] <---> amSrc[2] <--- $self[1] <--- perl-stack
76  *
77  * When the callback is invoked, a reference to the amglue_Source is placed on
78  * the perl stack, so it is once again referenced twice.
79  *
80  * -- $self->remove();
81  *               GSrc[1] <---) amSrc[1] <--- $self[1] <--- perl-stack
82  *
83  * Now the callback itself has called remove().  The amglue_Source object removes
84  * the GSource from the MainLoop and drops its reference to the perl callback, and
85  * decrements its refcount to again weaken the reference from the GSource.  The
86  * amglue_Source is now useless, but since it is still in scope, it remains
87  * allocated and accessible.
88  *
89  * -- return;
90  *
91  * When the callback returns, the last reference to SV is destroyed, reducing
92  * the reference count to the amglue_Source to zero, reducing the reference to
93  * the GSource to zero.  Everything is gone.
94  */
95
96 /* We use a glib 'dataset' to attach an amglue_Source to each GSource
97  * object.  This requires a Quark to describe the kind of data being
98  * attached.
99  *
100  * We define a macro and corresponding global to support access
101  * to our quark.  The compiler will optimize out all but the first
102  * conditional in each function, which is just as we want it. */
103 static GQuark _quark = 0;
104 #define AMGLUE_SOURCE_QUARK \
105     ( _quark?_quark:(_quark = g_quark_from_static_string("amglue_Source")) )
106
107 amglue_Source *
108 amglue_source_get(
109     GSource *gsrc,
110     GSourceFunc callback)
111 {
112     amglue_Source *src;
113     g_assert(gsrc != NULL);
114
115     src = (amglue_Source *)g_dataset_id_get_data(gsrc, AMGLUE_SOURCE_QUARK);
116
117     if (!src)
118         src = amglue_source_new(gsrc, callback);
119     else
120         amglue_source_ref(src);
121
122     return src;
123 }
124
125 amglue_Source *
126 amglue_source_new(
127     GSource *gsrc,
128     GSourceFunc callback)
129 {
130     amglue_Source *src = g_new0(amglue_Source, 1);
131     g_source_ref(gsrc);
132     src->src = gsrc;
133     src->callback = callback;
134     src->state = AMGLUE_SOURCE_NEW;
135     src->refcount = 1;
136     g_dataset_id_set_data(gsrc, AMGLUE_SOURCE_QUARK, (gpointer)src);
137
138     return src;
139 }
140
141 void
142 amglue_source_free(
143     amglue_Source *self)
144 {
145     /* if we're attached, we hold a circular reference to ourselves,
146      * so we shouldn't be at refcount=0 */
147     g_assert(self->state != AMGLUE_SOURCE_ATTACHED);
148     g_assert(self->callback_sv == NULL);
149
150     g_dataset_id_remove_data(self->src, AMGLUE_SOURCE_QUARK);
151     g_source_unref(self->src);
152     g_free(self);
153 }