So I could run this with some debugging tools, like AddressSanitizer, I did a manual conversion of the program to C, and it has no problems with only using WaitThread:
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
typedef struct TSignal
{
SDL_Condition *Condition;
SDL_Mutex *Mutex;
bool Flag;
} TSignal, *PSignal;
static void SignalInitialize (PSignal ASignal)
{
ASignal->Condition = SDL_CreateCondition();
ASignal->Mutex = SDL_CreateMutex();
ASignal->Flag = false;
}
static void SignalFinalize (PSignal ASignal)
{
SDL_DestroyCondition (ASignal->Condition);
SDL_DestroyMutex (ASignal->Mutex);
}
static void SignalEmit(PSignal ASignal)
{
SDL_LockMutex(ASignal->Mutex);
ASignal->Flag = true;
SDL_SignalCondition(ASignal->Condition);
SDL_UnlockMutex(ASignal->Mutex);
}
static void SignalWait (PSignal ASignal)
{
SDL_LockMutex(ASignal->Mutex);
while (!ASignal->Flag) {
SDL_WaitCondition(ASignal->Condition, ASignal->Mutex);
}
ASignal->Flag = false;
SDL_UnlockMutex(ASignal->Mutex);
}
#define FRAME_W 256
#define FRAME_H 240
#define THREAD_NUM_MAX FRAME_H
static const int
SQUARE_CENTER_X = FRAME_W / 2,
SQUARE_CENTER_Y = FRAME_H / 2,
SQUARE_SIZE = 32,
SQUARE_RADIUS = FRAME_H - SQUARE_CENTER_X - SQUARE_SIZE - 8;
typedef struct TThreadData
{
TSignal Enter;
TSignal Leave;
int Index;
bool Ended;
} TThreadData, *PThreadData;
static int ThreadNum;
static bool ThreadNumChanged;
static SDL_Thread *ThreadFunc[THREAD_NUM_MAX];
static TThreadData ThreadData[THREAD_NUM_MAX];
static float SquareAngle = 0.0;
static int SquareX;
static int SquareY;
static struct { Uint8 A, R, G, B; } FrameBuffer[FRAME_H][FRAME_W];
static int ThreadWorker (void *PVThreadData)
{
PThreadData AThreadData = (PThreadData) PVThreadData;
int IndexScanline;
int IndexPixel;
do {
SignalWait(&AThreadData->Enter);
if (!AThreadData->Ended) {
IndexScanline = AThreadData->Index;
while (IndexScanline < FRAME_H) {
for (IndexPixel = 0; IndexPixel < FRAME_W; IndexPixel++) {
FrameBuffer[IndexScanline][IndexPixel].R = IndexScanline ^ IndexPixel;
FrameBuffer[IndexScanline][IndexPixel].G = IndexScanline ^ IndexPixel;
FrameBuffer[IndexScanline][IndexPixel].B = IndexScanline ^ IndexPixel;
}
if ((IndexScanline > SquareY - SQUARE_SIZE) && (IndexScanline < SquareY + SQUARE_SIZE)) {
for (IndexPixel = SquareX - SQUARE_SIZE + 1; IndexPixel < SquareX + SQUARE_SIZE - 1; IndexPixel++) {
FrameBuffer[IndexScanline][IndexPixel].R = ~FrameBuffer[IndexScanline][IndexPixel].R;
FrameBuffer[IndexScanline][IndexPixel].G = ~FrameBuffer[IndexScanline][IndexPixel].G;
FrameBuffer[IndexScanline][IndexPixel].B = ~FrameBuffer[IndexScanline][IndexPixel].B;
}
}
IndexScanline += ThreadNum;
}
SignalEmit(&AThreadData->Leave);
}
} while (!AThreadData->Ended);
return 0;
}
static char Title[127];
static SDL_Window *Window;
static SDL_Renderer *Renderer;
static SDL_Texture *Texture;
static void *TextureData;
static int TexturePitch;
static void ThreadNumUpdate (int ANum)
{
int Index;
if (ANum > THREAD_NUM_MAX) { ANum = THREAD_NUM_MAX; }
if (ANum < 1) { ANum = 1; }
if (ANum == ThreadNum) { return; }
if (ANum > ThreadNum) {
for (Index = ThreadNum; Index < ANum; Index++) {
SignalInitialize(&ThreadData[Index].Enter);
SignalInitialize(&ThreadData[Index].Leave);
ThreadData[Index].Index = Index;
ThreadData[Index].Ended = false;
ThreadFunc[Index] = SDL_CreateThreadRuntime(ThreadWorker, "", &ThreadData[Index], NULL, NULL);
}
} else {
for (Index = ThreadNum - 1; Index >= ANum; Index--) {
ThreadData[Index].Ended = true;
SignalEmit(&ThreadData[Index].Enter);
SDL_WaitThread(ThreadFunc[Index], NULL);
SDL_DetachThread(ThreadFunc[Index]);
SignalFinalize(&ThreadData[Index].Enter);
SignalFinalize(&ThreadData[Index].Leave);
}
}
ThreadNum = ANum;
ThreadNumChanged = true;
}
int main(int argc, char **argv)
{
SDL_Event Event;
int FrameRate = 0;
int FrameRateNew = 0;
float FrameTime = 0.0;
Uint64 TimeSample;
Uint64 Second;
Uint64 SecondNew;
int Index;
SDL_Init(SDL_INIT_VIDEO);
SDL_memset(FrameBuffer, 0xFF, sizeof (FrameBuffer));
Second = SDL_GetTicks() / 1000;
SDL_snprintf(Title, sizeof (Title), "Threads: %d/%d â Frame rate: %d â Frame time: %.2fms", ThreadNum, THREAD_NUM_MAX, FrameRate, FrameTime);
Window = SDL_CreateWindow (Title, 640, 480, SDL_WINDOW_RESIZABLE);
Renderer = SDL_CreateRenderer (Window, "opengl");
Texture = SDL_CreateTexture (Renderer, SDL_PIXELFORMAT_BGRA8888, SDL_TEXTUREACCESS_STREAMING, FRAME_W, FRAME_H);
ThreadNum = SDL_GetNumLogicalCPUCores();
for (Index = 0; Index < ThreadNum; Index++) {
SignalInitialize(&ThreadData[Index].Enter);
SignalInitialize(&ThreadData[Index].Leave);
ThreadData[Index].Index = Index;
ThreadData[Index].Ended = false;
ThreadFunc[Index] = SDL_CreateThreadRuntime(ThreadWorker, "", &ThreadData[Index], NULL, NULL);
}
while (true) {
ThreadNumChanged = false;
while (SDL_PollEvent(&Event)) {
switch (Event.type) {
case SDL_EVENT_KEY_DOWN:
switch (Event.key.scancode) {
case SDL_SCANCODE_UP: ThreadNumUpdate(ThreadNum + 1); break;
case SDL_SCANCODE_DOWN: ThreadNumUpdate(ThreadNum - 1); break;
case SDL_SCANCODE_PAGEUP: ThreadNumUpdate(ThreadNum + 4); break;
case SDL_SCANCODE_PAGEDOWN: ThreadNumUpdate(ThreadNum - 4); break;
case SDL_SCANCODE_ESCAPE: goto l0001;
default: break;
}
break;
case SDL_EVENT_QUIT: goto l0001;
}
}
SquareAngle += (2 * SDL_PI_F) / (60 * 2);
SquareX = SDL_roundf(SQUARE_CENTER_X + SDL_cos(SquareAngle) * SQUARE_RADIUS);
SquareY = SDL_roundf(SQUARE_CENTER_Y + SDL_sin(SquareAngle) * SQUARE_RADIUS);
TimeSample = SDL_GetTicksNS();
//begin
for (Index = 0; Index < ThreadNum; Index++) { SignalEmit(&ThreadData[Index].Enter); }
for (Index = 0; Index < ThreadNum; Index++) { SignalWait(&ThreadData[Index].Leave); }
SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255);
SDL_RenderClear(Renderer);
SDL_LockTexture(Texture, NULL, &TextureData, &TexturePitch);
SDL_memcpy(TextureData, FrameBuffer, sizeof (FrameBuffer));
SDL_UnlockTexture(Texture);
SDL_RenderTexture(Renderer, Texture, NULL, NULL);
//end;
FrameTime = (SDL_GetTicksNS() - TimeSample) / 1000000;
SDL_RenderPresent(Renderer);
FrameRateNew += 1;
SecondNew = SDL_GetTicks() / 1000;
if ((ThreadNumChanged) || (SecondNew != Second)) {
if (SecondNew != Second) {
Second = SecondNew;
FrameRate = FrameRateNew;
FrameRateNew = 0;
}
SDL_snprintf(Title, sizeof (Title), "Threads: %d/%d â Frame rate: %d â Frame time: %.2fms", ThreadNum, THREAD_NUM_MAX, FrameRate, FrameTime);
SDL_SetWindowTitle(Window, Title);
ThreadNumChanged = false;
}
SDL_Delay(15);
}
l0001:
for (Index = 0; Index < ThreadNum; Index++) {
ThreadData[Index].Ended = true;
SignalEmit(&ThreadData[Index].Enter);
SDL_WaitThread(ThreadFunc[Index], NULL);
// SDL_DetachThread(ThreadFunc[Index]);
SignalFinalize(&ThreadData[Index].Enter);
SignalFinalize(&ThreadData[Index].Leave);
}
SDL_DestroyRenderer(Renderer);
SDL_DestroyWindow(Window);
SDL_DestroyTexture(Texture);
SDL_Quit();
return 0;
}
…no incorrect memory accesses, let alone crashes.
So it could be a Pascal binding thing, or it could be a bug in SDL’s Windows code (I’m on Linux here), but I guess knowing where it crashed (or even better, why it crashed) would be extremely helpful, if we can determine that somehow.