RandyGaul / cute_headers

Collection of cross-platform one-file C/C++ libraries with no dependencies, primarily used for games
4.24k stars 264 forks source link

cute_sync SDL implementation has intermittent segfaults #313

Open aganm opened 2 years ago

aganm commented 2 years ago

I have an issue with cute_threadpool_kick_and_wait resulting in intermittent segfaults when using the SDL implementation.

I allocate memory then I pass this memory to the task function to do some async computation with cute_threadpool_add_task. Then, I call cute_threadpool_kick_and_wait expecting the function to wait and join on all the added tasks to free all my memory. But when I do free all my memory, I get a segfault in the task function for accessing freed memory, meaning the task function is still running after calling cute_threadpool_kick_and_wait.

Am I misunderstanding what this function does? I'm expecting it to block the thread I am calling it from and wait until all the tasks are completed.

This is the code to reproduce:

#include <SDL.h>
#define CUTE_SYNC_IMPLEMENTATION
#define CUTE_SYNC_SDL
#include "cute_sync.h"
#include <stdio.h>
#include <stdlib.h>

typedef struct int_buffer {
    int count;
    int* ptr;
} int_buffer;

void job(void* userdata)
{
    int_buffer* ints = userdata;
    int count = ints->count;
    for (int i = 0; i < count; ++i) {
        ints->ptr[i] = i * count * 30;
    }
}

#define NUM 10

int main()
{
    const int count = 1000000;

    int_buffer ints[NUM];
    for (int i = 0; i < NUM; ++i) {
        ints[i].count = count;
        ints[i].ptr = malloc(sizeof(int) * count);
    }

    cute_threadpool_t* pool = cute_threadpool_create(11, NULL);

    for (int i = 0; i < NUM; ++i) {
        cute_threadpool_add_task(pool, job, &ints[i]);
    }

    /* wait for completion before freeing the memory */
    cute_threadpool_kick_and_wait(pool);

    for (int i = 0; i < NUM; ++i) {
        free(ints[i].ptr);
    }

    cute_threadpool_destroy(pool);

    return 0;
}
cc main.c -lSDL2 -I/usr/include/SDL2

If I run this code 10 times it will segfault about half of the time.

It doesn't segfault if I use the POSIX implementation making me think it's something specific to the SDL implementation.

iamwhosiam commented 3 months ago

This is 2 years old, you've probably already figured this out, but I had the same issue and I think I have figured it out. I believe the issue is that cute_threadpool_kick_and_wait() exits when all tasks are "started" and not when there finished. So when it exits the last couple of tasks are still being executed.

My hack solution is to have a boolean attached to each task that I set to true after the work is done and just check for that after the _kick_and_wait() but it would be nice if this was the way that the function worked in the first place.

RandyGaul commented 3 months ago

Hey awesome! I haven’t used this code for some time — if you have a PR that would be really cool :)