关于 YYAsyncLayer 可能出现的问题

先上YYAsyncLayer 源码链接

问题提出

在看@indulge_inYYAsyncLayer 源码剖析:异步绘制时看到:

要点 3 :轮询返回队列

使用原子自增函数OSAtomicIncrement32()对局部静态变量counter进行自增,然后通过取模运算轮询返回队列。

注意这里使用了一个判断:if (cur < 0) cur = -cur,当cur自增越界时就会变为负数最大值(在二进制层面,是用正整数的反码加一来表示其负数的)。

if (cur < 0) cur = -cur开始是觉得很巧妙,使用OSAtomicIncrement32()自增来均匀地取队列数组里面的队列,但是仔细一想,发现了一个问题。

cur的数据类型是int32_t,所以当自增到越界的时候就会变为负数的最大值,而我们知道,正数的最大值为2147483647,而负数的最大值为-2147483648,绝对值不一样是因为正数有0。

所以当cur自增越界时就会变为负数最大值-2147483648,而这时候直接通过if (cur < 0) cur = -cur;这个判断来取正肯定是有问题的,因为int32_t类型不存在2147483648

问题验证

cur值到底会变成什么值呢,这我就写了一个验证代码:

static int counter = INT32_MAX;

int cur = OSAtomicIncrement32(&counter);
if (cur < 0) {
    cur = -cur;
}
NSLog(@" %d", cur);

结果打印输出是还是-2147483648,我推测是因为无法置为负值,所以不变。如果有更好的答案,可以评论一下,谢谢。

再看源码:

if (cur < 0) cur = -cur;
return queues[(cur) % queueCount];

cur取正失败则会导致取余以后的数的值也可能出现负值,因为-2147483648是2的31次方,如果queueCount不是2的倍数,则取余的值就不是0,而出现负值。

而根据源码:

queueCount = (int)[NSProcessInfo processInfo].activeProcessorCount;
queueCount = queueCount < 1 ? 1 : queueCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueCount;

而经过查阅,A11 和 A12 芯片 CPU 都是6核心并且能所有核心同时工作,所以有风险会出现余数为负值。

下面通过简单改写源码验证负值情况:

static dispatch_queue_t YYAsyncLayerGetDisplayQueue() {
#define MAX_QUEUE_COUNT 16
    static int queueCount = 6;
    static dispatch_queue_t queues[MAX_QUEUE_COUNT];
    static dispatch_once_t onceToken;
    static int32_t counter = INT32_MAX - 5;
    dispatch_once(&onceToken, ^{
        for (NSUInteger i = 0; i < queueCount; i++) {
            dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
            queues[i] = dispatch_queue_create("com.ibireme.yykit.render", attr);
        }
    });
    int32_t cur = OSAtomicIncrement32(&counter);
    if (cur < 0) cur = -cur;
    int remainder = cur%queueCount;
    NSLog(@" %d", remainder);
    return queues[remainder];
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        for (NSInteger i = 0; i < 10; i++) {
            dispatch_queue_t queue = YYAsyncLayerGetDisplayQueue();
            dispatch_async(queue, ^{
                NSLog(@"");
            });
        }
    }
    return 0;
}

这段代码的结果是,当cur自增越界为-2147483648时,取余后的余数为-2,而此时调用YYAsyncLayerGetDisplayQueue返回queue时出现EXC_BAD_ACCESS崩溃。

问题解决

下面来讲一下如何解决这个问题。在发现这个问题之后我就去找了 YYDispatchQueuePool 库看看是否有同样的问题,因为YYAsyncLayer也是在本地没有YYDispatchQueuePool的情况下才使用这段代码。结果是并没有这个问题,先看代码:

static dispatch_queue_t YYDispatchContextGetQueue(YYDispatchContext *context) {
    uint32_t counter = (uint32_t)OSAtomicIncrement32(&context->counter);
    void *queue = context->queues[counter % context->queueCount];
    return (__bridge dispatch_queue_t)(queue);
}

YYDispatchQueuePoolOSAtomicIncrement32()自增调用后会强制转换为uint32_t所有就完全不会出现负值的情况,就算自增越界后也会变为0。所以counter不会出现负值,余数也不会出现负值,就完全没有以上的问题了。所以,只需要参照YYDispatchQueuePool将代码:

int32_t cur = OSAtomicIncrement32(&counter);
if (cur < 0) cur = -cur;

改为

uint32_t cur = (uint32_t)OSAtomicIncrement32(&counter);

就可以解决此问题。

已经提交 PullRequest https://github.com/ibireme/YYAsyncLayer/pull/21

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容