Is there any issue if local std::function out of its "life"?

Viewed 125

Could you please help to review the below code? is there any issue if a local std::function is out of its "life"? Thanks in advance.

class Test {
    public:
    void commit(std::function<void()> func)
    {
        // return immediately
        dispatch_async(conqueue, ^{
            // in order to call func when it is out of "life"
            sleep(5);
            func(); // is there any issue? the func should be invalid at this moment?
        });
    }

    void test() {
        std::string name = "test";
        std::function<void()> func = [name, this] () {
            printf("%lx %s\n", &name, name.c_str());
            std::cout << "callback"<<std::endl;
        };
        // async
        commit(func);
    }
    //...
};
2 Answers

OK, I ran some tests and have changed my view on this. The code is safe because func is captured by value (i.e. the block inherits a copy).

I proved this to myself with the following test program (you will need MacOS to run this):

// clang block_test.mm -lc++

#include <iostream>
#include <unistd.h>
#include <dispatch/dispatch.h>

struct Captured
{
    Captured () {}
    Captured (const Captured &c) { std::cout << "Copy constructor\n"; }
    void Foo () const { std::cout << "Foo\n"; }
};

int main ()
{
    Captured c;

    // return immediately
    dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
    ^{
        sleep(5);
        c.Foo ();
     });
    
    sleep (10);
};

Output:

Copy constructor
Copy constructor
Foo

I'm not sure why the copy constructor is called twice though. Ask Apple.

Update: There's a good writeup on working with blocks here. That's what that funny 'hat' syntax is.

It's safe. Apple "Blocks" capture non-__block variables by value, so the block contains a copy of the std::function inside it, so the lifetime of the block's copy of the variable is the same as the block itself.

(P.S. Even if the variable was declared __block, it would still be safe. Blocks capture __block variables kind of "by reference", in that multiple blocks, as well as the original function scope, share the state of the variable. However, the underlying mechanism of __block ensures that the variable is moved to the heap if it is used by a block that outlives the function's scope. So for a __block variable, its lifetime lasts until all blocks that captured it have been destroyed.)

Related