Merge pull request #4401 from minggo/thread-helper

[ci skip]add helper funcion to invoke a function in gl thread
This commit is contained in:
minggo 2013-11-27 22:26:11 -08:00
commit 3268da3a4d
8 changed files with 185 additions and 223 deletions

View File

@ -1026,9 +1026,6 @@ void DisplayLinkDirector::startAnimation()
}
_invalid = false;
#ifndef EMSCRIPTEN
Application::getInstance()->setAnimationInterval(_animationInterval);
#endif // EMSCRIPTEN
}
void DisplayLinkDirector::mainLoop()
@ -1040,6 +1037,9 @@ void DisplayLinkDirector::mainLoop()
}
else if (! _invalid)
{
// invoke call back from other thread
ThreadHelper::doCallback();
drawScene();
// release the objects

View File

@ -162,10 +162,6 @@ void TextureCache::loadImage()
while (true)
{
// create autorelease pool for iOS
Thread thread;
thread.createAutoreleasePool();
std::queue<AsyncStruct*> *pQueue = _asyncStructQueue;
_asyncStructQueueMutex.lock();
if (pQueue->empty())

View File

@ -24,23 +24,62 @@ THE SOFTWARE.
#include "CCThread.h"
NS_CC_BEGIN
// iOS and Mac already has a Thread.mm
#if (CC_TARGET_PLATFORM != CC_PLATFORM_IOS && CC_TARGET_PLATFORM != CC_PLATFORM_MAC)
NS_CC_BEGIN
std::list<std::function<void(void)>>* ThreadHelper::_callbackList = new std::list<std::function<void(void)>>();
std::mutex* ThreadHelper::_mutex = new std::mutex;
long ThreadHelper::_callbackNumberPerFrame = 5;
Thread::~Thread()
void* ThreadHelper::createAutoreleasePool()
{
// To prevent warning: private field '_autoreasePool' is not
// used [-Wunused-private-field] by CLANG.
_autoReleasePool = nullptr;
return nullptr;
}
void Thread::createAutoreleasePool()
void ThreadHelper::releaseAutoreleasePool(void* autoreleasePool)
{
}
NS_CC_END
void ThreadHelper::runOnGLThread(std::function<void(void)> f)
{
// Insert call back function
_mutex->lock();
_callbackList->push_back(f);
_mutex->unlock();
}
void ThreadHelper::doCallback()
{
_mutex->lock();
auto iter = _callbackList->begin();
long i = 0;
while (iter != _callbackList->end())
{
auto f = *iter;
f();
++i;
if (i >= _callbackNumberPerFrame)
{
break;
}
else
{
iter = _callbackList->erase(iter);
}
}
_mutex->unlock();
}
void ThreadHelper::setCallbackNumberPerFrame(long callbackNumberPerFrame)
{
_callbackNumberPerFrame = callbackNumberPerFrame;
}
#endif
NS_CC_END

View File

@ -25,8 +25,12 @@ THE SOFTWARE.
#ifndef __CC_PLATFORM_THREAD_H__
#define __CC_PLATFORM_THREAD_H__
#include <functional>
#include <list>
#include <mutex>
#include "platform/CCCommon.h"
#include "CCPlatformMacros.h"
#include "CCDirector.h"
NS_CC_BEGIN
@ -39,27 +43,45 @@ NS_CC_BEGIN
* and release it when the thread end.
*/
class CC_DLL Thread
class CC_DLL ThreadHelper
{
public:
/**
friend DisplayLinkDirector;
/** Create an autorelease pool for objective-c codes.
* @js NA
* @lua NA
*/
Thread() : _autoReleasePool(nullptr) {}
static void* createAutoreleasePool();
/**
* @js NA
* @lua NA
*/
~Thread();
/**
*/
static void releaseAutoreleasePool(void *autoreleasePool);
/** To run a function in gl thread.
* @js NA
* @lua NA
@since v3.0
*/
void createAutoreleasePool();
static void runOnGLThread(std::function<void(void)> f);
/** Set how many callback functions being invoked per frame. Default value is 5.
* @js NA
* @lua NA
@since v3.0
*/
static void setCallbackNumberPerFrame(long callbackNumberPerFrame);
private:
void *_autoReleasePool;
// This function will be call by Director to call some call back function on gl thread
static void doCallback();
static std::list<std::function<void(void)>> *_callbackList;
static std::mutex *_mutex;
// How many callback functions invoked per frame
static long _callbackNumberPerFrame;
};
// end of platform group

View File

@ -26,14 +26,55 @@ THE SOFTWARE.
NS_CC_BEGIN
Thread::~Thread()
std::list<std::function<void(void)>>* ThreadHelper::_callbackList = new std::list<std::function<void(void)>>();
std::mutex* ThreadHelper::_mutex = new std::mutex;
long ThreadHelper::_callbackNumberPerFrame = 5;
void* ThreadHelper::createAutoreleasePool()
{
[(id)_autoReleasePool release];
id pool = [[NSAutoreleasePool alloc] init];
return pool;
}
void Thread::createAutoreleasePool()
void ThreadHelper::releaseAutoreleasePool(void *autoreleasePool)
{
_autoReleasePool = [[NSAutoreleasePool alloc] init];
[(NSAutoreleasePool*)autoreleasePool release];
}
void ThreadHelper::runOnGLThread(std::function<void(void)> f)
{
// Insert call back function
_mutex->lock();
_callbackList->push_back(f);
_mutex->unlock();
}
void ThreadHelper::doCallback()
{
_mutex->lock();
auto iter = _callbackList->begin();
long i = 0;
while (iter != _callbackList->end())
{
auto f = *iter;
f();
++i;
if (i >= _callbackNumberPerFrame)
{
break;
}
else
{
iter = _callbackList->erase(iter);
}
}
_mutex->unlock();
}
void ThreadHelper::setCallbackNumberPerFrame(long callbackNumberPerFrame)
{
_callbackNumberPerFrame = callbackNumberPerFrame;
}
NS_CC_END

View File

@ -154,10 +154,6 @@ void DataReaderHelper::loadData()
while (true)
{
// create autorelease pool for iOS
Thread thread;
thread.createAutoreleasePool();
std::queue<AsyncStruct *> *pQueue = _asyncStructQueue;
_asyncStructQueueMutex.lock(); // get async struct from queue
if (pQueue->empty())

View File

@ -86,15 +86,10 @@ AssetsManager::AssetsManager(const char* packageUrl/* =NULL */, const char* vers
, _shouldDeleteDelegateWhenExit(false)
{
checkStoragePath();
_schedule = new Helper();
}
AssetsManager::~AssetsManager()
{
if (_schedule)
{
_schedule->release();
}
if (_shouldDeleteDelegateWhenExit)
{
delete _delegate;
@ -161,7 +156,10 @@ bool AssetsManager::checkUpdate()
if (res != 0)
{
sendErrorMessage(ErrorCode::NETWORK);
ThreadHelper::runOnGLThread([&, this]{
if (this->_delegate)
this->_delegate->onError(ErrorCode::NETWORK);
});
CCLOG("can not get version file content, error code is %d", res);
curl_easy_cleanup(_curl);
return false;
@ -170,7 +168,10 @@ bool AssetsManager::checkUpdate()
string recordedVersion = UserDefault::getInstance()->getStringForKey(keyOfVersion().c_str());
if (recordedVersion == _version)
{
sendErrorMessage(ErrorCode::NO_NEW_VERSION);
ThreadHelper::runOnGLThread([&, this]{
if (this->_delegate)
this->_delegate->onError(ErrorCode::NO_NEW_VERSION);
});
CCLOG("there is not new version");
// Set resource search path.
setSearchPath();
@ -190,25 +191,45 @@ void AssetsManager::downloadAndUncompress()
{
if (! downLoad()) break;
// Record downloaded version.
AssetsManager::Message *msg1 = new AssetsManager::Message();
msg1->what = ASSETSMANAGER_MESSAGE_RECORD_DOWNLOADED_VERSION;
msg1->obj = this;
_schedule->sendMessage(msg1);
ThreadHelper::runOnGLThread([&, this]{
UserDefault::getInstance()->setStringForKey(this->keyOfDownloadedVersion().c_str(),
this->_version.c_str());
UserDefault::getInstance()->flush();
});
}
// Uncompress zip file.
if (! uncompress())
{
sendErrorMessage(ErrorCode::UNCOMPRESS);
ThreadHelper::runOnGLThread([&, this]{
if (this->_delegate)
this->_delegate->onError(ErrorCode::UNCOMPRESS);
});
break;
}
// Record updated version and remove downloaded zip file
AssetsManager::Message *msg2 = new AssetsManager::Message();
msg2->what = ASSETSMANAGER_MESSAGE_UPDATE_SUCCEED;
msg2->obj = this;
_schedule->sendMessage(msg2);
ThreadHelper::runOnGLThread([&, this] {
// Record new version code.
UserDefault::getInstance()->setStringForKey(this->keyOfVersion().c_str(), this->_version.c_str());
// Unrecord downloaded version code.
UserDefault::getInstance()->setStringForKey(this->keyOfDownloadedVersion().c_str(), "");
UserDefault::getInstance()->flush();
// Set resource search path.
this->setSearchPath();
// Delete unloaded zip file.
string zipfileName = this->_storagePath + TEMP_PACKAGE_FILE_NAME;
if (remove(zipfileName.c_str()) != 0)
{
CCLOG("can not remove downloaded zip file %s", zipfileName.c_str());
}
if (this->_delegate) this->_delegate->onSuccess();
});
} while (0);
_isDownloading = false;
@ -452,18 +473,20 @@ static size_t downLoadPackage(void *ptr, size_t size, size_t nmemb, void *userda
int assetsManagerProgressFunc(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded)
{
AssetsManager* manager = (AssetsManager*)ptr;
AssetsManager::Message *msg = new AssetsManager::Message();
msg->what = ASSETSMANAGER_MESSAGE_PROGRESS;
static int percent = 0;
int tmp = (int)(nowDownloaded / totalToDownload * 100);
ProgressMessage *progressData = new ProgressMessage();
progressData->percent = (int)(nowDownloaded/totalToDownload*100);
progressData->manager = manager;
msg->obj = progressData;
if (percent != tmp)
{
percent = tmp;
ThreadHelper::runOnGLThread([=]{
auto manager = static_cast<AssetsManager*>(ptr);
if (manager->_delegate)
manager->_delegate->onProgress(percent);
});
manager->_schedule->sendMessage(msg);
CCLOG("downloading... %d%%", (int)(nowDownloaded/totalToDownload*100));
CCLOG("downloading... %d%%", percent);
}
return 0;
}
@ -475,7 +498,10 @@ bool AssetsManager::downLoad()
FILE *fp = fopen(outFileName.c_str(), "wb");
if (! fp)
{
sendErrorMessage(ErrorCode::CREATE_FILE);
ThreadHelper::runOnGLThread([&, this]{
if (this->_delegate)
this->_delegate->onError(ErrorCode::CREATE_FILE);
});
CCLOG("can not create file %s", outFileName.c_str());
return false;
}
@ -492,7 +518,10 @@ bool AssetsManager::downLoad()
curl_easy_cleanup(_curl);
if (res != 0)
{
sendErrorMessage(ErrorCode::NETWORK);
ThreadHelper::runOnGLThread([&, this]{
if (this->_delegate)
this->_delegate->onError(ErrorCode::NETWORK);
});
CCLOG("error when download package");
fclose(fp);
return false;
@ -560,133 +589,6 @@ unsigned int AssetsManager::getConnectionTimeout()
return _connectionTimeout;
}
void AssetsManager::sendErrorMessage(AssetsManager::ErrorCode code)
{
Message *msg = new Message();
msg->what = ASSETSMANAGER_MESSAGE_ERROR;
ErrorMessage *errorMessage = new ErrorMessage();
errorMessage->code = code;
errorMessage->manager = this;
msg->obj = errorMessage;
_schedule->sendMessage(msg);
}
// Implementation of AssetsManagerHelper
AssetsManager::Helper::Helper()
{
_messageQueue = new list<Message*>();
Director::getInstance()->getScheduler()->scheduleUpdateForTarget(this, 0, false);
}
AssetsManager::Helper::~Helper()
{
Director::getInstance()->getScheduler()->unscheduleAllForTarget(this);
delete _messageQueue;
}
void AssetsManager::Helper::sendMessage(Message *msg)
{
_messageQueueMutex.lock();
_messageQueue->push_back(msg);
_messageQueueMutex.unlock();
}
void AssetsManager::Helper::update(float dt)
{
Message *msg = NULL;
// Returns quickly if no message
_messageQueueMutex.lock();
if (0 == _messageQueue->size())
{
_messageQueueMutex.unlock();
return;
}
//remove unnecessary message
std::list<Message*>::iterator it;
Message *proMsg = nullptr;
for (it = _messageQueue->begin(); it != _messageQueue->end(); ++it)
{
if((*it)->what == ASSETSMANAGER_MESSAGE_PROGRESS)
{
if (proMsg)
{
_messageQueue->remove(proMsg);
delete (ProgressMessage*)proMsg->obj;
delete proMsg;
}
proMsg = *it;
}
}
// Gets message
msg = *(_messageQueue->begin());
_messageQueue->pop_front();
_messageQueueMutex.unlock();
switch (msg->what) {
case ASSETSMANAGER_MESSAGE_UPDATE_SUCCEED:
handleUpdateSucceed(msg);
break;
case ASSETSMANAGER_MESSAGE_RECORD_DOWNLOADED_VERSION:
UserDefault::getInstance()->setStringForKey(((AssetsManager*)msg->obj)->keyOfDownloadedVersion().c_str(),
((AssetsManager*)msg->obj)->_version.c_str());
UserDefault::getInstance()->flush();
break;
case ASSETSMANAGER_MESSAGE_PROGRESS:
if (((ProgressMessage*)msg->obj)->manager->_delegate)
{
((ProgressMessage*)msg->obj)->manager->_delegate->onProgress(((ProgressMessage*)msg->obj)->percent);
}
delete (ProgressMessage*)msg->obj;
break;
case ASSETSMANAGER_MESSAGE_ERROR:
// error call back
if (((ErrorMessage*)msg->obj)->manager->_delegate)
{
((ErrorMessage*)msg->obj)->manager->_delegate->onError(((ErrorMessage*)msg->obj)->code);
}
delete ((ErrorMessage*)msg->obj);
break;
default:
break;
}
delete msg;
}
void AssetsManager::Helper::handleUpdateSucceed(Message *msg)
{
AssetsManager* manager = (AssetsManager*)msg->obj;
// Record new version code.
UserDefault::getInstance()->setStringForKey(manager->keyOfVersion().c_str(), manager->_version.c_str());
// Unrecord downloaded version code.
UserDefault::getInstance()->setStringForKey(manager->keyOfDownloadedVersion().c_str(), "");
UserDefault::getInstance()->flush();
// Set resource search path.
manager->setSearchPath();
// Delete unloaded zip file.
string zipfileName = manager->_storagePath + TEMP_PACKAGE_FILE_NAME;
if (remove(zipfileName.c_str()) != 0)
{
CCLOG("can not remove downloaded zip file %s", zipfileName.c_str());
}
if (manager->_delegate) manager->_delegate->onSuccess();
}
AssetsManager* AssetsManager::create(const char* packageUrl, const char* versionFileUrl, const char* storagePath, ErrorCallback errorCallback, ProgressCallback progressCallback, SuccessCallback successCallback )
{
class DelegateProtocolImpl : public AssetsManagerDelegateProtocol

View File

@ -166,41 +166,8 @@ protected:
bool uncompress();
bool createDirectory(const char *path);
void setSearchPath();
void sendErrorMessage(ErrorCode code);
void downloadAndUncompress();
private:
typedef struct _Message
{
public:
_Message() : what(0), obj(NULL){}
unsigned int what; // message type
void* obj;
} Message;
class Helper : public cocos2d::Object
{
public:
/**
* @js ctor
*/
Helper();
/**
* @js NA
* @lua NA
*/
~Helper();
virtual void update(float dt);
void sendMessage(Message *msg);
private:
void handleUpdateSucceed(Message *msg);
std::list<Message*> *_messageQueue;
std::mutex _messageQueueMutex;
};
private:
/** @brief Initializes storage path.
*/
@ -224,7 +191,6 @@ private:
void *_curl;
Helper *_schedule;
unsigned int _connectionTimeout;
AssetsManagerDelegateProtocol *_delegate;