How to handle this pointer that was destroyed in another thread?

Now, I have a problem with lambdas, as you can see in the codes below, if this pointer ( the HelloWorld node) destroy, the code in lambdas will crash the app.

Could I solve this problem with std::shared_ptr? Could someone give me some suggestions regarding this problem? Thanks,

bool HelloWorld::init()
{

    if ( !Scene::init() )
    {
        return false;
    }
    
    auto visibleSize = Director::getInstance()->getVisibleSize();
    auto origin = Director::getInstance()->getVisibleOrigin();
	
	auto label = Label::createWithTTF("Hello World", "fonts/arial.ttf", TITLE_FONT_SIZE);

    label->setPosition(Vec2::ZERO);

    this->addChild(label, 1);
	
	auto downloader = new (std::nothrow) network::Downloader();
		
	downloader->setOnTaskError([this](const network::DownloadTask& task, int error_code, int error_internal_code, const std::string& error_string) {
	
	});

	downloader->onTaskProgress = [this](const network::DownloadTask& task,int64_t /*bytesReceived*/,int64_t totalBytesReceived, int64_t totalBytesExpected) {

	};

	downloader->onDataTaskSuccess = [this,label,origin,visibleSize](const cocos2d::network::DownloadTask& task, std::vector<unsigned char>& buffer) {

		Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]() {
		
			//the "this" pointer destroyed before that lambda is invoked.
			//so the app will crash here 
			if (auto image = utils::findChild<ui::ImageView*>(this, "image")) {

				image->loadTexture("test.png");
			}
			
			//same with above
			if(label) {
				
				 label->setPosition(origin.x + visibleSize.width/2,origin.y + visibleSize.height - label->getContentSize().height);
			}

		});
	};

}

Several ways to fix this issue.

If your object, HelloWorld, is being destroyed, then the downloader object should also be destroyed, correct? Downloader does not inherit from CC:Ref, so you’re responsible for release memory associated with it.

A possible solution is to make downloader a member variable of HelloWorld, and in the destructor of “HelloWorld”, free it. You may need to set all download callbacks to nullptr before you free it as well.

Make cocos2d::network::Downloader a smart pointer to simplify things (in HelloWorld.h):

std::unique_ptr<cocos2d::network::Downloader> _downloader = nullptr;

Then in HelloWorld.cpp:

// in init etc
_downloader = std::make_unique<cocos2d::network::Downloader>();

// Destructor
HelloWorld::~HelloWorld()
{
    _downloader->setOnTaskError = nullptr;
    _downloader->onTaskProgress = nullptr;
    _downloader->onDataTaskSuccess = nullptr;
    _downloader = nullptr; // explicitly free smart pointer
}

You may need a lock on it, but I’m not entirely sure. The reason being that the callbacks may trigger while it’s in the middle of the destructor, before it clears the callback pointers.

1 Like