méthode sûre pour attendre que tous les rappels du minuteur soient terminés

En cas de timer ponctuelle, je peux utiliser un sémaphore pour attendre la fin du rappel de la timer. Mais si la timer a été déclenchée plusieurs fois, cela n’aidera pas. Considérons le code suivant:

#include  #include  #include  #include  #include  #include  #include  #define N 10 void timer_threaded_function(sigval_t si) { uint8_t *shared_resource = si.sival_ptr; sleep(rand() % 7); /* ... manipulate with shared_resource */ return; } int main() { struct sigevent sig_ev = {0}; uint8_t *shared_resource = malloc(123); timer_t timer_id; int i; sig_ev.sigev_notify = SIGEV_THREAD; sig_ev.sigev_value.sival_ptr = shared_resource; sig_ev.sigev_notify_function = timer_threaded_function; sig_ev.sigev_notify_atsortingbutes = NULL; timer_create(CLOCK_REALTIME, &sig_ev, &timer_id); for (i = 0; i < N; i++) { /* arm timer for 1 nanosecond */ timer_settime(timer_id, 0, &(struct itimerspec){{0,0},{0,1}}, NULL); /* sleep a little bit, so timer will be fired */ usleep(1); } /* only disarms timer, but timer callbacks still can be running */ timer_delete(timer_id); /* * TODO: safe wait for all callbacks to end, so shared resource * can be freed without races. */ ... free(shared_resource); return 0; } 

timer_delete () désarme uniquement le timer (s’il était armé) et libère les ressources associées au timer. Mais les rappels de timer peuvent toujours être en cours d’exécution. Nous ne pouvons donc pas libérer de shared_resource, sans quoi une condition de concurrence critique pourrait se produire. Y at-il une méthode pour faire face à cette situation?

Je pense au comptage des références, mais cela n’aide en rien, car nous ne soaps pas combien de threads essaieront réellement d’accéder aux ressources partagées (cause des dépassements de timer).

Il est totalement insatisfaisant :-(. J’ai regardé, et il ne semble pas y avoir de moyen de savoir si le sigevent (a) n’a pas été congédié, ou (b) est en attente, ou (c) est en cours d’exécution ou ( d) a terminé.

Le mieux que je puisse suggérer est un niveau supplémentaire d’indirection et un sharepoint repère statique pour pointer sur la ressource partagée. Alors:

  static foo_t* p_shared ; .... p_shared = shared_resourse ; ..... sig_ev.sigev_value.sival_ptr = &p_shared ; 

foo_t est le type de la ressource partagée.

Nous pouvons maintenant utiliser des atomes … dans timer_threaded_function() :

  foo_t** pp_shared ; foo_t* p_locked ; foo_t* p_shared ; pp_shared = so.sival_ptr ; p_locked = (void*)UINPTR_MAX ; p_shared = atomic_swap(pp_shared, p_locked) ; if (p_shared == p_locked) return ; // locked already. if (p_shared == NULL) return ; // destroyed already. .... proceed to do the usual work ... if (atomic_cmp_swap(pp_shared, &p_locked, p_shared)) return ; // was locked and is now restored assert(p_locked == NULL) ; ... the shared resource needs to be freed ... 

Et dans le fil de contrôle:

  timer_delete(timer_id) ; // no more events, thank you p_s = atomic_swap(&p_shared, NULL) ; // stop processing events if (p_s == (void*)UINTPTR_MAX) // an event is being processed. if (p_s != NULL) ... the shared resource needs to be freed ... 

Lorsque le thread d’événement constate que le recours partagé doit être libéré, il peut le faire lui-même ou signaler au thread de contrôle que l’événement a été traité, de sorte que le thread de contrôle puisse poursuivre et libérer. C’est en grande partie une question de goût.

Fondamentalement, il s’agit d’utiliser atomics pour fournir une sorte de verrou dont la valeur est sorting-state: NULL <=> détruit; UINTPTR_MAX <=> verrouillé; toute autre chose <=> débloquée.

L’ static p_shared est le static p_shared , qui doit restr en place jusqu’à ce que la timer_threaded_function() soit terminée et ne soit plus jamais appelé … :-(.