mutter/cogl/tests/conform/test-journal.c

47 lines
1.3 KiB
C
Raw Permalink Normal View History

cogl/journal: Don't sometimes hold a ref on the framebuffer d42f1873fcd0876244eb8468d72ce35459ba94ca introduced a semi circular reference between the CoglFramebuffer, and CoglJournal, where CoglJournal would keep a reference on the CoglFramebuffer when there were any entries in the journal log. To avoid risking leaking these objects indefinitely, when freeing objects without doing anything that triggered a flush, CoglFramebuffer had a "filter" on cogl_object_unref() calls, which knew about under what conditions CoglJournal had a reference to it. When it could detect that there were only the journal itself holding such a reference, it'd flush the journal, effectively releasing the reference the journal held, thus freeing itself, as well as the journal. When CoglFramebuffer was ported to be implemented using GObject instead of CoglObject, this "filter" was missed, causing not only awkward but infrequent leaks, but also situations where we'd flush journals when only the journal itself held the last reference to the framebuffer, meaning the journal would free the framebuffer, thus itself, in the middle of flushing, causing memory corruption and crashes. A way to detect this, by asserting on CoglObject reference count during flush, is by adding the `g_assert()` as described below, which will assert instead cause memory corruption. void _cogl_journal_flush (CoglJournal *journal { ... _cogl_journal_discard (journal); + g_assert (journal->_parent.ref_count > 0); ... } Fix this by making CoglFramebuffer the owner of the journal, which it already was, and remove any circle referencing that was there before, as it is not needed given that the CoglFramebuffer pointer is guaranteed to be valid for the lifetime of CoglJournal as the framebuffer is the owner of the journal. However, to not miss flushing before tearing down, which is important as this flushes painting calls to the driver that is important for e.g. using the result of those journal entries, flush the journal the first time cogl_framebuffer_dispose() is called, before doing anything else. This also adds a test case. Without having broken the circular reference, the test would fail on g_assert_null (offscreen), as it would have been "leaked" at this point, but the actual memory corruption would be a result of the `cogl_texture_get_data()` call, which flushes the framebuffer, and causes the 'mid-flush' destruction of the journal described above. Note that the texture keeps track of dependent framebuffers, but it does not hold any references to them. Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1474 Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1735>
2021-02-18 09:46:28 -05:00
#include <cogl/cogl.h>
#include <stdio.h>
#include <string.h>
#include "test-declarations.h"
#include "test-utils.h"
void
test_journal_unref_flush (void)
{
CoglTexture2D *texture;
CoglOffscreen *offscreen;
CoglPipeline *pipeline;
const int width = 1;
const int height = 1;
const int stride = width * 4;
uint8_t reference_data[] = {
0x33, 0x33, 0x33, 0x33,
};
uint8_t data[G_N_ELEMENTS (reference_data)];
G_STATIC_ASSERT (sizeof data == sizeof reference_data);
texture = cogl_texture_2d_new_with_size (test_ctx, width, height);
offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture));
g_object_add_weak_pointer (G_OBJECT (offscreen), (gpointer *) &offscreen);
pipeline = cogl_pipeline_new (test_ctx);
cogl_pipeline_set_color4ub (pipeline, 0x33, 0x33, 0x33, 0x33);
cogl_framebuffer_draw_rectangle (COGL_FRAMEBUFFER (offscreen),
pipeline,
-1, -1, 1, 1);
cogl_object_unref (pipeline);
g_object_unref (offscreen);
g_assert_null (offscreen);
cogl_texture_get_data (COGL_TEXTURE (texture),
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
stride, data);
g_assert_cmpmem (data, sizeof (data),
reference_data, sizeof (reference_data));
cogl_object_unref (texture);
}