Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is callback hell in C++ and why will it leak memory?

Tags:

c++

I've been watching Herb Sutter's CppCon 2016 talk, in which he gave an example around 37 minutes in, something like this:

void f(shared_ptr<T> & ptr)
{
    obj.on_draw([=]() { ... }
}

He then says,

I've heard it called callback hell, where you register a callback and it has a strong owner--it happens to be a garbage-collected pointer but it's a strong owner--but then you never get rid of it and it's just stored there forever and now the object will never go away.

So he is saying that it's called callback hell and it will leak objects. But I don't quite understand what's wrong with this code and why it will leak. Could someone explain this to me?

I've looked at others answers on stackoverflow, but they all seem to be about concurrency.

like image 988
Виталик Бушаев Avatar asked Nov 03 '16 22:11

Виталик Бушаев


1 Answers

What Herb Sutter is talking about are circular references. He promotes a layered system, where resources aren't passed down to code that "reaches up"

Layer 1 - Owns the resources and Objects from Layer 2 (and below)

Layer 2 - Can`t have strong references to Layer 1 objects

What this ensures is, that the graph of dependencies don't get circles. So if Layer 1 frees all Layer 2 Objects then all resources get destroyed. Why this is is important is quite easy: The resource counting from the C++ Std library can't deal with circular references (no ref-counting can), if obj a has a strong reference to obj b and obj b has a strong reference to obj a, then they will never be freed.

The ugly thruth is that this also is a problem if the circle goes over multiple references, possibly through software modules of different authors. Without a scheme like layers, you can`t just look at the code and say "There is no chance this will end up referencing the object I am calling from". Herb Sutter proposes a convention, that unless you know the implementation you should never call a function that might keep a resource alive.

This is not saying that you should never do it, but if you follow a set of rules you can verify code per-layer or even per-file without knowing the rest of the system indepth. Otherwise you`d have to find all possible paths the function (on_draw) could take, to see if circular depencies could result - and if anything changes in any of the code possibly touched, then you have to do it again!

In that context "callback hell" is especially problematic since it kinda circumvents the type-system (not possibly to just allow interfaces from lower levels), and the callback could do anything.

If the callback doesnt save a reference to a resource, then use a plain pointer instead, this explicitely states to the caller that he doesnt need to worry about leaks. Not now or in the future.

like image 52
Norbert Lange Avatar answered Oct 17 '22 04:10

Norbert Lange