Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SDL_AddTimer and threading, clearing timer queue

I'm writing a media player based on FFmpeg and I have a design issue. My code calls a function to render video frames at irregular intervals using SDL's SDL_AddTimer function. My problem is this, if I want to perform garbage collection when one video ends (or playback is stopped) and another starts, how can I ensure that I have no more timers in the queue or in the middle of execution as to avoid accessing an object that suddenly has been freed.

like image 535
William Seemann Avatar asked Oct 18 '22 08:10

William Seemann


1 Answers

There's a lot of different ways to solve your problem based on your specific implementation. Few basic ideas can be:

  1. Define a boolean that specifies if the player is running (true = running, false = not)

  2. Before each call related to the player, check that variable and if its false (the player is stopped), close the function (immediately return)

  3. Then, you call removeTimer and you set the variable to false to close the player.

However, because addTimer is a function that creates threads, there can be sometimes race conditions(what you called "the middle of execution"). It's one of the main problem when you use threads.

You need to find and fix those by using tools such as mutexes, semaphores, ... The choice of those tools greatly depends on the strategy you want to implement to avoid those race conditions.

So, there is no generic solution. If performance is not a concern, you can use that simple "one-lock" solution. It's a basic implementation of the "critical section" strategy. It prevents some pieces of code (closing player and running critical sound-related calls) to run simultaneously.

#include <pthread.h>
#include <stdbool.h>

pthread_mutex_t mutex; 
bool isRunning; 
int timerID; 

void timerCallback(){
    // do some computations 

    pthread_mutex_lock(&mutex); 
    // that critical section, defined here will never 
    // (thank to the mutex) occur at the same time at 
    // the critical section in the close method 

    if(isRunning){
         // do critical calls
    }
    else{
         // do nothing
    }
    pthread_mutex_unlock(&mutex); 

    // do some other computations
}

void init(){
    pthread_mutex_init(&mutex, NULL); 
    isRunning = true; 
    startTimers(); 
}

void close(){
    pthread_mutex_lock(&mutex); 

    // that critical section, defined here will never 
    // (thank to the mutex) occur at the same time at 
    // the critical section in the timerCallback method 
    SDL_RemoveTimer(timerID); 
    isRunning = false; 
    pthread_mutex_unlock(&mutex); 

    pthread_mutex_destroy(&mutex); 
}

Of course, I cannot explain all the race-condition avoidance strategies in a simple stackoverflow post. If you need to improve performance, you should define precisely what's your concurrency issue for your code and choose a right strategy.

like image 169
Alexis Clarembeau Avatar answered Oct 21 '22 06:10

Alexis Clarembeau