Is SDL Timer Callback Interrupt or MultiThread?

Hi im new of this forum.
Currently, Im using SDLTimer for game programming.
To avoid errors, I found I need to do the Mutual exclusion.
So I want to know how do I do mutual exclusion for SDLTimer callbacks.
At first, these callbacks are running on a separate thread,
Does main thread also work while callbacks are working?

I’m begginer of programming. sry if my explanation is’nt enough.
I found a similar thread on this forum, but I couldn’t make out it

The docs for SDL_AddTimer says

The callback is run on a separate thread.

which means it’s “multithreaded”. I.e. the main function and the callback runs on different threads and might execute code at the same time.

The same way as with all multithreaded code. There are a few different techniques but I belive the most common ones are to use mutexes (locks) to protect shared data or to use atomic variables.

I don’t want to discourage you from learning multithreading, but personally, I don’t consider multithreading to be a beginner topic. It’s one of the most easy things to get wrong and debugging it can be a nightmare. It essentially requires that you know what you’re doing because testing might not always reveal a problem. My advice is to avoid multithreading unless you have a very good reason for using it, and in that case make sure you restrict it to as small part of the code as possible (minimize the amount of shared state and the amount of code that deals with it).

1 Like

Thank you for your answer.
Currently, I use 2 callback funcs and a main func.
one is for game processing, another one is for the rendering. main func for the initializing and SDLevent.
I want to make game processing and rendering separeted.

yes… I k its gonna be a hard way, but I wanna use multithreading. Of course I won’t use shared data so much as long as I can.

Just a heads up in case you’re not aware…

Can I call SDL video functions from multiple threads?
No, most graphics back ends are not thread-safe, so you should only call SDL video functions from the main thread of your application.
SDL2/FAQDevelopment - SDL Wiki

Not sure if this is going to change in SDL3.

1 Like

Excuse me.

No, most graphics back ends are not thread-safe, so you should only call SDL video functions from the main thread of your application.

Is this a problem even if I use rendering funcs only on the rendering thread but not a main thread??
I mean I don’t call SDL rendering funcs on the other threads except 1.

Otherwise I will combain it to the main thread immediately.

From a recent discussion about adding better thread-safety documentation:

Just throwing this idea out there:

If you just generally need some amount of time to go by, and precision isn’t super-important, you can take a basic program that looks like this:

while (program_is_still_running) {
    check_for_new_events();
    do_stuff();
    draw_frame();
}

…and make it look like this instead…

 Uint32 next_time_to_do_something = 0;

while (program_is_still_running) {

    // THIS IS THE NEW THING.
    const Uint64 now = SDL_GetTicks64();  // milliseconds program has been running.
    if (now >= next_time_to_do_something) {
        do_something_when_timer_is_up();
        next_time_to_do_something = SDL_GetTicks64() + how_long_to_wait_for_timers;
    }
    // THAT'S THE NEW THING.


    check_for_new_events();
    do_stuff();
    draw_frame();
}

Another option is to push a user-defined event onto the event queue from your AddTimer handler, and deal with that event from the main thread with all your other events, but this risks a little bit of lag (but almost always, it’s tolerable, and always, you should write code that can tolerate it).

Rather than pushing an event, I would prefer the approach in the pseudo code above, though.

2 Likes

Go for it! My app is multi-threaded and it works fine on all platforms, even Emscripten (although multithreading does limit the browsers that will work, currently iOS browsers don’t support it).

Of course you have to be careful, and you can only make renderer-related calls from one thread, but I didn’t encounter any specific problems. SDL2 contains all the synchronising primitives you are likely to need (semaphores, mutexes etc.).

I don’t know what version of iOS started supporting them, so it might be too new to reasonably count on, but they definitely work on my iPhone running iOS 16.6.

Here’s a thing using Emscripten with pthreads. The rendering happens in the main thread, and a background thread is generating the pixels for the green bar. (This is not a serious app, it was just to see if we could get dlopen and pthreads to work on the web with our game engine).

https://icculus.org/~icculus/emscripten/cext_pixel_array-html5-1.0/

In that case can you try this link on your phone, please. It works in all desktop browsers plus Chrome and Firefox on Android, but I’ve not successfully opened it in any iOS browser (‘Exception thrown, see JavaScript console’).

Rebuild with -s MAXIMUM_MEMORY=1gb when linking and see if it fixes it.

Unbelievable - it works! But why? It’s not necessary on any other platform, including Android. Are there any downsides?

The only downside is you can’t allocate more than a gigabyte of memory. :slight_smile:

The exception was because it’s trying to allocate the SharedArrayBuffer and Safari/ios refuses to make one with a default maximum of 4 gigabytes (even if you don’t plan to actually allocate anything near that amount).

I ran into this same problem in DragonRuby Game Toolkit, so I knew exactly what to do when I saw it in the debugger for your app.

1 Like

So I don’t need to use rendering functions on the main thread, right?

Past time, I actually put drawing functions at main thread
but idk why, compared with callback threads, this method make it a bit lagging.
I gave objects speed and let them move. but that was not look like smooth.
this loop will takes 17msec but sometime it takes 30~60msec.
My code is like this.

int max_fps=60;
while(event.type!=SDL_QUIT){
	prev_frame_tick=SDL_GetTicks64();
	drawing();

	while(SDL_GetTicks64()-prev_frame_tick<1000/max_fps){
		SDL_WaitEventTimeout(&event,1000/max_fps-(SDL_GetTicks64()-prev_frame_tick));
		if(event.type==SDL_QUIT){
			break;
		}
		KeyEventProcess();
	}			
}

And When I drag the window, main thread process stoped.
These won’t be happened when I use callback.

Uint32 callback(Uint32 interval,void *pointer){
	drawing();
	return 1000/max_fps;
}

(eventfunctions on the main thread)
These are why I wanna use timer callback for rendering.

Oh, I didn’t have any idea to do this method.
Thank you!
I will try it if I gave up learning for multi threads.

I edited this code just now
because I thought I shouldn’t use SDL_Delay();

I think it’s been said that some platforms require rendering to be done in the ‘main’ thread (Emscripten for example). So whether it’s acceptable to render in a different thread (and only that thread) will depend on what platforms you want to support.

My advice is not to take the risk, and render only in the main thread. I do that in my app, and there’s no lag (but it is true that rendering stops when dragging the window).

Alright, I need to make out why my program is lagging.
Thank you for your answer everyone.

Things to check:

  • Ensure you have specified SDL_RENDERER_PRESENTVSYNC in your SDL_CreateRenderer() call.
  • Use while (SDL_PollEvent(...)) rather than if (SDL_PollEvent(...)) to ensure you drain all pending events every frame.

Ideally the only ‘waiting’ should be in SDL_RenderPresent(), not SDL_Delay() or SDL_WaitEventTimeout() etc.

2 Likes