[cocos2d-x]CCHttpClient的一个bug

公司的新游戏《我是大官人》马上就要大规模PR了,一切都已经准备就绪,这时测试部门却反馈了一个小问题,打开游戏的时候,偶尔会卡在启动界面,提示:正在连接服务器...然后就没反应了,这个问题发生的概率很低,大概3%左右,而且退出重新打开游戏就好了,“应该是网络不好造成的”,大家并没有太重视这个bug,但是老板不放心,“就算是网络问题,也不应该卡住,如果是新玩家碰到这种情况就直接流失了,这个问题得查一下。”
看来这不是一个小问题,于是这个bug分配给了我。

花了一些时间重现,我发现问题出在CCHttpClient,熟悉cocos2d-x的同学知道,这是一个异步的网络库,它的工作原理是这样的:HttpClient内部有一个工作线程,游戏主线程调用send()函数,将http请求压入一个队列,然后唤醒工作线程就返回了。工作线程被唤醒后,将http请求从队列中取出,再调用libcurl进行处理,然后再通过回调将结果返回主线程。bug重现的时候,主线程注册的回调没有触发,游戏就卡住了。我估计是libcurl将工作线程卡住了,于是在工作线程中加入了一些打印进行验证,结果却大意料:工作线程根本就没有触发!

又是一个多线程同步的问题!这是工作线程的代码片段:

// Worker thread
static void* networkThread(void *data)
{
    CCHttpRequest *request = NULL;

    while (true)
    {
        if (need_quit)
        {
            break;
        }

        // step 1: send http request if the requestQueue isn't empty
        request = NULL;

        pthread_mutex_lock(&s_requestQueueMutex); //Get request task from queue
        if (0 != s_requestQueue->count())
        {
            request = dynamic_cast<CCHttpRequest*>(s_requestQueue->objectAtIndex(0));
            s_requestQueue->removeObjectAtIndex(0);
            // request's refcount = 1 here
        }
        pthread_mutex_unlock(&s_requestQueueMutex);

        if (NULL == request)
        {
        	// Wait for http request tasks from main thread
        	pthread_cond_wait(&s_SleepCondition, &s_SleepMutex);
            continue;
        }

        // step 2: libcurl sync access

工作线程空闲的时候,通过pthread_cond_wait()挂起

send()函数的代码:

//Add a get task to queue
void CCHttpClient::send(CCHttpRequest* request)
{
    if (false == lazyInitThreadSemphore())
    {
        return;
    }

    if (!request)
    {
        return;
    }

    ++s_asyncRequestCount;

    request->retain();

    pthread_mutex_lock(&s_requestQueueMutex);
    s_requestQueue->addObject(request);
    pthread_mutex_unlock(&s_requestQueueMutex);

    // Notify thread start to work
    pthread_cond_signal(&s_SleepCondition);
}

我仔细的看了一遍,发现这句话很奇怪:

pthread_cond_wait(&s_SleepCondition, &s_SleepMutex);

这里用了一个互斥锁s_SleepMutex,而这个锁没有其他代码使用。这很可能是一个错误:一个互斥锁如果只有一处代码使用,那么只有一种可能,这段代码会在多个线程中执行。而httpClient的线程显然只有一个。google了一番,果然是这个互斥锁用错了。大家可以参考知乎上的一篇文章:http://www.zhihu.com/question/24116967

里面详细解释了为什么没有正确的加锁会导致信号量丢失。

这里是修改后的代码,不再使用单独的s_SleepMutex, 用requestQueueMutex代替

// Worker thread
static void* networkThread(void *data)
{
    CCHttpRequest *request = NULL;

    while (true)
    {
        if (need_quit)
        {
            break;
        }

        // step 1: send http request if the requestQueue isn't empty
        request = NULL;
        pthread_mutex_lock(&s_requestQueueMutex); //Get request task from queue
        while (0 == s_requestQueue->count()) {
            pthread_cond_wait(&s_SleepCondition, &s_requestQueueMutex);
        }
        request = dynamic_cast<CCHttpRequest*>(s_requestQueue->objectAtIndex(0));
        s_requestQueue->removeObjectAtIndex(0);
                // request's refcount = 1 here

        CCLog("s_SleepCondition notified");
        pthread_mutex_unlock(&s_requestQueueMutex);
        

反复测试了一个下午,这个bug没有再复现了。

这是在cocos2d-x 2.1.4版本上改的,cocos2d-x 3.x没有使用pthread_cond_wait进行线程同步,但也存在类似的问题。

时间: 2024-10-13 20:35:22

[cocos2d-x]CCHttpClient的一个bug的相关文章

Ibatis2.3.4的一个bug

java.lang.ClassCastException: com.chat.upgrade.domain.ClientFile cannot be cast to java.lang.String 今天查一个对象转化成json串报错的问题,查了两个小时,最后问题的根源居然是ibatis. ibatis的语句如下: <typeAlias alias="Client" type="com.chat.upgrade.domain.ClientFile"/>

Win10系统菜单打不开问题的解决,难道是Win10的一个Bug ?

Win10左下角菜单打不开,好痛苦,点击右下角的时间也没反应,各种不爽,折磨了我好几天,重装又不忍心,实在费劲,一堆开发环境要安装,上网找了很多方法都不适用.今天偶然解决了,仔细想了下,难道是Win10的一个Bug? 1.问题和现象 右下角菜单点不开,下面的状态栏的右键也没有反应.时间日期也点不开,音频喇叭同样点不开....各种烦人,百度一堆都无果.... 说明:Win10是正式版,已激活:杀毒也全盘扫描过,因为电脑是开发和办公用,几乎不上其他网站,所以中毒的可能性几乎为0. 2.解决方法 晚上

Universal-Image-Loader的一个BUG

使用UIL的内置圆角图片的功能时,发现一个BUG,就是它会拉伸图片,造成图片失真.费了一下午的功夫,重写了RoundedBitmapDisplayer,总算解决这个问题. 代码如下: public class RoundedBitmapDisplayer implements BitmapDisplayer { protected final int cornerRadius; protected final int margin; public RoundedBitmapDisplayer(i

ubuntu12.04 software-center 的一个BUG

ubuntu software-center 软件中心今天突然发现打不开了,就是在启动的过程中启动一半就退出了,多次启动无果.首先想到的办法当然是最彻底的两句话 sudo apt-get purge software-center sudo apt-get install software-center 结果未果,启动起来还是首先一个窗体初始化 接着..就直接关闭了.然后查看它的输出信息,发现原来是py输出中文导致的,因为我们窗体上有很多中文字体的组件需要加载,而python处理的时候有一个使用

docker 1.0.0发布以及一个bug依赖apparmor_parser

6月10号docker 1.0稳定版本发布,找了台ubuntu的机器,装了下 ubuntu version:12.04 docker version:1.0.0 装docker的步骤可以看官方文档:https://docs.docker.com/installation/ubuntulinux/ 装好之后,运行docker -d尝试启动docker守护进程,报错如下: [0fcb4ed6] +job serveapi(tcp://127.0.0.1:2375) [0fcb4ed6] +job i

【Qt】无边框窗体中带有ActiveX组件时的一个BUG

无意中发现的一个BUG,Qt5.1.1正式版首先创建一个GUI工程,拖入一个QAxWidget控件(为了使ActiveX生效,需要在.pro文件中加入CONFIG += qaxcontainer)接着,为了让ActiveX有效,需要引入一个组件,我这里引入的是IE组件 [cpp] view plain copy MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->s

使用getDrawable时遇到的一个bug

做一个筛选菜单时候,用到了dongjunkun的DropDownMenu,github地址:https://github.com/dongjunkun/DropDownMenu 遇到几个问题: (1)最右面的上三角形.下三角形很难看,需要改成向上箭头向下箭头,而且靠近文件,在右边: (2)背景颜色需要改成白色: (3)下面的子菜单的文字在最左边,需要居中: (4)第一次进来Fragment的时候DropDownMenu的下拉选项没有选中任意一项 上面几个需求看起来很容易改,不就是改改布局什么的,

memory_limit的一个bug | 风雪之隅

原文:memory_limit的一个bug | 风雪之隅 27 Nov 09 memory_limit的一个bug 作者: Laruence( ) 本文地址: http://www.laruence.com/2009/11/27/1164.html 转载请注明出处 PHP 5.2x中, 由于错误的选用了zend_atoi, 导致memory_limit不能设置为超过4G的值. 今天同事分享给我一个问题(thans to yanmi), 一段代码(PHP 5.2.11 Linux/X86_64),

记用ajax的一个bug

遇到一个bug,IE8,只有第一次功能正确,后面都不生效.我看了一下,确实IE存在问题,调试发现根本不进入后台,于是我猜测是缓存的问题,于是加上cache:false,解决了问题. $.ajax( { url: "ParaSetting/OpenOrCloseParam", data: { Id: id }, cache:false, type: "get", success: function (data) { alert(data); if (data ===