添加printf语句后Realloc触发“invalid next size”错误排查求助
嘿,我帮你仔细过了一遍代码,发现了几个很可能导致堆损坏的问题,尤其是第一个错误几乎肯定就是你遇到的core dump的元凶!
1. 初始化task_queue时的memset越界(最关键问题)
看你sdm_threadpool_create函数里这段代码:
pool->task_queue = malloc(sizeof(sdm_threadpool_task_t) * queue_capacity); if (pool->task_queue == NULL) { fprintf(stderr, "Memory allocation problem. Quiting\n"); exit(1); } memset(pool->task_queue, 0, pool->num_threads * sizeof(pool->task_queue[0]));
你犯了一个低级但致命的错误:malloc是按queue_capacity分配的空间,但memset却用pool->num_threads来计算要初始化的字节数!
举个例子:如果DEFAULT_QUEUE_LENGTH(初始队列长度)比NUM_THREADS小,memset会直接写超过malloc分配的内存范围,把堆的元数据彻底破坏。堆结构被破坏后,后续的realloc调用就会检测到异常,抛出realloc(): invalid next size的错误。
那为什么注释掉printf就没事?其实不是真的没事,只是堆损坏的症状没被触发而已——printf会放慢sdm_threadpool_add的执行速度,改变线程处理任务的时序,刚好让realloc在堆损坏后立刻被调用,从而暴露问题;没有printf的时候,时序刚好避开了触发检测的时机,但堆其实已经被破坏了,只是没立刻崩溃。
修正方法:把memset的计数改成queue_capacity:
memset(pool->task_queue, 0, queue_capacity * sizeof(pool->task_queue[0]));
2. 队列扩容的逻辑错误
再看sdm_threadpool_add里的扩容循环:
while (pool->waiting_in_queue >= pool->queue_capacity) { pool->queue_capacity *= 2; size_t new_size = sizeof(sdm_threadpool_task_t) * pool->queue_capacity; printf("Extending queue to %zu bytes\n", new_size); sdm_threadpool_task_t *new_task_queue = realloc(pool->task_queue, new_size); if (new_task_queue == NULL) { fprintf(stderr, "Memory allocation problem. Quiting\n"); exit(1); } pool->task_queue = new_task_queue; }
你用waiting_in_queue >= pool->queue_capacity作为扩容条件完全错误。waiting_in_queue是还没被线程取走的任务数,而queue_capacity是队列的总容量。实际上,你需要检查的是已添加的任务总数(也就是pool->queue_length)是否超过了队列容量——因为你是用线性数组实现队列,所有已添加的任务都存在数组里,不管线程有没有取走。
如果线程处理任务的速度很快,waiting_in_queue可能永远达不到queue_capacity,但此时queue_length已经超过了队列实际容量,你往task_queue[queue_length]写数据时会越界,再次破坏堆结构。
修正方法:把扩容条件改成检查queue_length,而且用if就够了(因为每次扩容翻倍,一次就足够容纳当前所有任务):
if (pool->queue_length >= pool->queue_capacity) { pool->queue_capacity *= 2; size_t new_size = sizeof(sdm_threadpool_task_t) * pool->queue_capacity; printf("Extending queue to %zu bytes\n", new_size); sdm_threadpool_task_t *new_task_queue = realloc(pool->task_queue, new_size); if (new_task_queue == NULL) { fprintf(stderr, "Memory allocation problem. Quiting\n"); exit(1); } pool->task_queue = new_task_queue; }
3. 额外的潜在问题(非当前崩溃原因,但需注意)
最后提两个不导致当前崩溃,但有隐患的点:
- 线性队列的空间浪费:线程处理任务时只递增
next_in_queue,从不复用前面已处理完的任务空间,会导致队列数组越变越大,即使大部分空间已经没用。后续可以改成环形队列优化。 sdm_threadpool_join的轮询不安全:用usleep轮询waiting_in_queue是否为0,不仅效率低,而且线程不安全——你在没有持有锁的情况下读取waiting_in_queue,可能读到过期的值。正确做法是用条件变量等待:在sdm_threadpool_join里持有锁,循环等待waiting_in_queue == 0,用pthread_cond_wait挂起线程,而非主动轮询。
把前两个错误修复后,你再打开printf应该就不会出现core dump了。堆损坏问题很多时候看起来是随机的,但本质都是越界写内存破坏了堆结构,只要找到越界的地方就能解决。
备注:内容来源于stack exchange,提问作者smolloy

