async ready callback doesn't clean delegate target (memory leak)
Vala code:
async void func() {
}
void main() {
MainLoop ml = new MainLoop();
AsyncReadyCallback cb = (obj, res) => {
func.end(res);
ml.quit();
};
func.begin((owned)cb);
ml.run();
}
Generated C code:
static void
__lambda4_ (Block1Data* _data1_,
GObject* obj,
GAsyncResult* res)
{
g_return_if_fail (res != NULL);
func_finish (res);
g_main_loop_quit (_data1_->ml);
}
static void
___lambda4__gasync_ready_callback (GObject* source_object,
GAsyncResult* res,
gpointer self)
{
__lambda4_ (self, source_object, res);
}
void
_vala_main (void)
{
Block1Data* _data1_;
GMainLoop* _tmp0_;
GAsyncReadyCallback cb = NULL;
gpointer cb_target;
GDestroyNotify cb_target_destroy_notify;
GAsyncReadyCallback _tmp1_;
gpointer _tmp1__target;
GDestroyNotify _tmp1__target_destroy_notify;
_data1_ = g_slice_new0 (Block1Data);
_data1_->_ref_count_ = 1;
_tmp0_ = g_main_loop_new (NULL, FALSE);
_data1_->ml = _tmp0_;
cb = ___lambda4__gasync_ready_callback;
cb_target = block1_data_ref (_data1_);
cb_target_destroy_notify = block1_data_unref;
_tmp1_ = cb;
_tmp1__target = cb_target;
_tmp1__target_destroy_notify = cb_target_destroy_notify;
cb = NULL;
cb_target = NULL;
cb_target_destroy_notify = NULL;
func (_tmp1_, _tmp1__target);
g_main_loop_run (_data1_->ml);
(cb_target_destroy_notify == NULL) ? NULL : (cb_target_destroy_notify (cb_target), NULL);
cb = NULL;
cb_target = NULL;
cb_target_destroy_notify = NULL;
block1_data_unref (_data1_);
_data1_ = NULL;
}
The reference counter of the Block1Data object here is incremented, as it becomes a target for the cb
delegate object. But it's never decremented later when ___lambda4__gasync_ready_callback
finally arrives. Therefore valgrind complains due to the memory leak.
Using a semantically identical variation where the closure is used directly as delegate however leads to correct memory cleanup. Vala code:
async void func() {
}
void main() {
MainLoop ml = new MainLoop();
func.begin((obj, res) => {
func.end(res);
ml.quit();
});
ml.run();
}
Generated C code:
static void
___lambda4__gasync_ready_callback (GObject* source_object,
GAsyncResult* res,
gpointer self)
{
__lambda4_ (self, source_object, res);
block1_data_unref (self);
}
void
_vala_main (void)
{
Block1Data* _data1_;
GMainLoop* _tmp0_;
_data1_ = g_slice_new0 (Block1Data);
_data1_->_ref_count_ = 1;
_tmp0_ = g_main_loop_new (NULL, FALSE);
_data1_->ml = _tmp0_;
func (___lambda4__gasync_ready_callback, block1_data_ref (_data1_));
g_main_loop_run (_data1_->ml);
block1_data_unref (_data1_);
_data1_ = NULL;
}
Here a block1_data_unref (self)
line is added to ___lambda4__gasync_ready_callback
and hence the Block1Data object is freed correctly.