summaryrefslogtreecommitdiff
path: root/ext/wayland/wlbuffer.c
blob: 4ac99ef4b0b6a5931cce8f33dcccc18ef8adfdc0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/* GStreamer Wayland video sink
 *
 * Copyright (C) 2014 Collabora Ltd.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 */

/* GstWlBuffer wraps wl_buffer and provides a mechanism for preventing
 * buffers from being re-used while the compositor is using them. This
 * is achieved by adding a reference to the GstBuffer as soon as its
 * associated wl_buffer is sent to the compositor and by removing this
 * reference as soon as the compositor sends a wl_buffer::release message.
 *
 * This mechanism is a bit complicated, though, because it adds cyclic
 * references that can be dangerous. The reference cycles looks like:
 *
 *   ----------------
 *   | GstWlDisplay | ---------------------------->
 *   ----------------                              |
 *                                                 |
 *                                                 V
 *   -----------------     -------------     ---------------
 *   | GstBufferPool | --> | GstBuffer | ==> | GstWlBuffer |
 *   |               | <-- |           | <-- |             |
 *   -----------------     -------------     ---------------
 *
 * A GstBufferPool normally holds references to its GstBuffers and each buffer
 * holds a reference to a GstWlBuffer (saved in the GstMiniObject qdata).
 * When a GstBuffer is in use, it holds a reference back to the pool and the
 * pool doesn't hold a reference to the GstBuffer. When the GstBuffer is unrefed
 * externally, it returns back to the pool and the pool holds again a reference
 * to the buffer.
 *
 * Now when the compositor is using a buffer, the GstWlBuffer also holds a ref
 * to the GstBuffer, which prevents it from returning to the pool. When the
 * last GstWlBuffer receives a release event and unrefs the last GstBuffer,
 * the GstBufferPool will be able to stop and if no-one is holding a strong
 * ref to it, it will be destroyed. This will destroy the pool's GstBuffers and
 * also the GstWlBuffers. This will all happen in the same context of the last
 * gst_buffer_unref, which will be called from the buffer_release() callback.
 *
 * The problem here lies in the fact that buffer_release() will be called
 * from the event loop thread of GstWlDisplay, so it's as if the display
 * holds a reference to the GstWlBuffer, but without having an actual reference.
 * When we kill the display, there is no way for the GstWlBuffer, the associated
 * GstBuffer and the GstBufferPool to get destroyed, so we are going to leak a
 * fair ammount of memory.
 *
 * Normally, this rarely happens, because the compositor releases buffers
 * almost immediately and when waylandsink stops, they are already released.
 *
 * However, we want to be absolutely certain, so a solution is introduced
 * by registering all the GstWlBuffers with the display and explicitly
 * releasing all the buffer references as soon as the display is destroyed.
 *
 * When the GstWlDisplay is finalized, it takes a reference to all the
 * registered GstWlBuffers and then calls gst_wl_buffer_force_release_and_unref,
 * which releases the potential reference to the GstBuffer, destroys the
 * underlying wl_buffer and removes the reference that GstWlDisplay is holding.
 * At that point, either the GstBuffer is alive somewhere and still holds a ref
 * to the GstWlBuffer, which it will release when it gets destroyed, or the
 * GstBuffer was destroyed in the meantime and the GstWlBuffer gets destroyed
 * as soon as we remove the reference that GstWlDisplay holds.
 */

#include "wlbuffer.h"

GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug);
#define GST_CAT_DEFAULT gstwayland_debug

G_DEFINE_TYPE (GstWlBuffer, gst_wl_buffer, G_TYPE_OBJECT);

static G_DEFINE_QUARK (GstWlBufferQDataQuark, gst_wl_buffer_qdata);

static void
gst_wl_buffer_dispose (GObject * gobject)
{
  GstWlBuffer *self = GST_WL_BUFFER (gobject);

  GST_TRACE_OBJECT (self, "dispose");

  /* if the display is shutting down and we are trying to dipose
   * the GstWlBuffer from another thread, unregister_buffer() will
   * block and in the end the display will increase the refcount
   * of this GstWlBuffer, so it will not be finalized */
  if (self->display)
    gst_wl_display_unregister_buffer (self->display, self);

  G_OBJECT_CLASS (gst_wl_buffer_parent_class)->dispose (gobject);
}

static void
gst_wl_buffer_finalize (GObject * gobject)
{
  GstWlBuffer *self = GST_WL_BUFFER (gobject);

  GST_TRACE_OBJECT (self, "finalize");

  if (self->wlbuffer)
    wl_buffer_destroy (self->wlbuffer);

  G_OBJECT_CLASS (gst_wl_buffer_parent_class)->finalize (gobject);
}

static void
gst_wl_buffer_class_init (GstWlBufferClass * klass)
{
  GObjectClass *object_class = (GObjectClass *) klass;

  object_class->dispose = gst_wl_buffer_dispose;
  object_class->finalize = gst_wl_buffer_finalize;
}

static void
gst_wl_buffer_init (GstWlBuffer * self)
{
}

static void
buffer_release (void *data, struct wl_buffer *wl_buffer)
{
  GstWlBuffer *self = data;

  GST_LOG_OBJECT (self, "wl_buffer::release (GstBuffer: %p)", self->gstbuffer);

  self->used_by_compositor = FALSE;

  /* unref should be last, because it may end up destroying the GstWlBuffer */
  gst_buffer_unref (self->gstbuffer);
}

static const struct wl_buffer_listener buffer_listener = {
  buffer_release
};

static void
gstbuffer_disposed (GstWlBuffer * self)
{
  g_assert (!self->used_by_compositor);
  self->gstbuffer = NULL;

  GST_TRACE_OBJECT (self, "owning GstBuffer was finalized");

  /* this will normally destroy the GstWlBuffer, unless the display is
   * finalizing and it has taken an additional reference to it */
  g_object_unref (self);
}

GstWlBuffer *
gst_buffer_add_wl_buffer (GstBuffer * gstbuffer, struct wl_buffer *wlbuffer,
    GstWlDisplay * display)
{
  GstWlBuffer *self;

  self = g_object_new (GST_TYPE_WL_BUFFER, NULL);
  self->gstbuffer = gstbuffer;
  self->wlbuffer = wlbuffer;
  self->display = display;

  gst_wl_display_register_buffer (self->display, self);

  wl_buffer_add_listener (self->wlbuffer, &buffer_listener, self);

  gst_mini_object_set_qdata ((GstMiniObject *) gstbuffer,
      gst_wl_buffer_qdata_quark (), self, (GDestroyNotify) gstbuffer_disposed);

  return self;
}

GstWlBuffer *
gst_buffer_get_wl_buffer (GstBuffer * gstbuffer)
{
  return gst_mini_object_get_qdata ((GstMiniObject *) gstbuffer,
      gst_wl_buffer_qdata_quark ());
}

void
gst_wl_buffer_force_release_and_unref (GstWlBuffer * self)
{
  /* Force a buffer release.
   * At this point, the GstWlDisplay has killed its event loop,
   * so we don't need to worry about buffer_release() being called
   * at the same time from the event loop thread */
  if (self->used_by_compositor) {
    GST_DEBUG_OBJECT (self, "forcing wl_buffer::release (GstBuffer: %p)",
        self->gstbuffer);
    self->used_by_compositor = FALSE;
    gst_buffer_unref (self->gstbuffer);
  }

  /* Finalize this GstWlBuffer early.
   * This method has been called as a result of the display shutting down,
   * so we need to stop using any wayland resources and disconnect from
   * the display. The GstWlBuffer stays alive, though, to avoid race
   * conditions with the GstBuffer being destroyed from another thread.
   * The last reference is either owned by the GstBuffer or by us and
   * it will be released at the end of this function. */
  GST_TRACE_OBJECT (self, "finalizing early");
  wl_buffer_destroy (self->wlbuffer);
  self->wlbuffer = NULL;
  self->display = NULL;

  /* remove the reference that the caller (GstWlDisplay) owns */
  g_object_unref (self);
}

void
gst_wl_buffer_attach (GstWlBuffer * self, struct wl_surface *surface)
{
  g_return_if_fail (self->used_by_compositor == FALSE);

  wl_surface_attach (surface, self->wlbuffer, 0, 0);

  /* Add a reference to the buffer. This represents the fact that
   * the compositor is using the buffer and it should not return
   * back to the pool and be re-used until the compositor releases it. */
  gst_buffer_ref (self->gstbuffer);
  self->used_by_compositor = TRUE;
}