If a thread is cancelled while it holds a mutex, other threads waiting for the mutex will never be woken, even if destroying the mutex, thus leading to a deadlock. Better just make the thread loops have a proper exit case than risking the tricky semantic of pthread_cancel.master
| static bool my_stop_is_required = false; | static bool my_stop_is_required = false; | ||||
| static pthread_cond_t my_cond_stop_is_acknowledged; | static pthread_cond_t my_cond_stop_is_acknowledged; | ||||
| static bool my_stop_is_acknowledged = false; | static bool my_stop_is_acknowledged = false; | ||||
| static bool my_terminate_is_required = 0; | |||||
| // my_thread: polls the audio duration and compares it to the duration of the first event. | // my_thread: polls the audio duration and compares it to the duration of the first event. | ||||
| static pthread_t my_thread; | static pthread_t my_thread; | ||||
| static bool thread_inited; | static bool thread_inited; | ||||
| { | { | ||||
| (void)p; // unused | (void)p; // unused | ||||
| while (1) { | |||||
| while (my_terminate_is_required) { | |||||
| bool a_stop_is_required = false; | bool a_stop_is_required = false; | ||||
| (void)pthread_mutex_lock(&my_mutex); | (void)pthread_mutex_lock(&my_mutex); | ||||
| my_event_is_running = false; | my_event_is_running = false; | ||||
| while (my_start_is_required == false) { | |||||
| while (my_start_is_required == false && my_terminate_is_required == false) { | |||||
| while ((pthread_cond_wait(&my_cond_start_is_required, &my_mutex) == -1) && errno == EINTR) | while ((pthread_cond_wait(&my_cond_start_is_required, &my_mutex) == -1) && errno == EINTR) | ||||
| continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
| } | } | ||||
| pthread_mutex_unlock(&my_mutex); | pthread_mutex_unlock(&my_mutex); | ||||
| // In this loop, my_event_is_running = true | // In this loop, my_event_is_running = true | ||||
| while (head && (a_stop_is_required == false)) { | |||||
| while (head && (a_stop_is_required == false) && (my_terminate_is_required == false)) { | |||||
| espeak_EVENT *event = (espeak_EVENT *)(head->data); | espeak_EVENT *event = (espeak_EVENT *)(head->data); | ||||
| assert(event); | assert(event); | ||||
| (void)pthread_mutex_unlock(&my_mutex); | (void)pthread_mutex_unlock(&my_mutex); | ||||
| if (a_stop_is_required == true) { | |||||
| if (a_stop_is_required == true || my_terminate_is_required == true) { | |||||
| // no mutex required since the stop command is synchronous | // no mutex required since the stop command is synchronous | ||||
| // and waiting for my_cond_stop_is_acknowledged | // and waiting for my_cond_stop_is_acknowledged | ||||
| init(); | init(); | ||||
| void event_terminate() | void event_terminate() | ||||
| { | { | ||||
| if (thread_inited) { | if (thread_inited) { | ||||
| pthread_cancel(my_thread); | |||||
| my_terminate_is_required = true; | |||||
| pthread_cond_signal(&my_cond_start_is_required); | |||||
| pthread_cond_signal(&my_cond_stop_is_required); | |||||
| pthread_join(my_thread, NULL); | pthread_join(my_thread, NULL); | ||||
| my_terminate_is_required = false; | |||||
| pthread_mutex_destroy(&my_mutex); | pthread_mutex_destroy(&my_mutex); | ||||
| pthread_cond_destroy(&my_cond_start_is_required); | pthread_cond_destroy(&my_cond_start_is_required); | ||||
| pthread_cond_destroy(&my_cond_stop_is_required); | pthread_cond_destroy(&my_cond_stop_is_required); |
| static bool my_command_is_running = false; | static bool my_command_is_running = false; | ||||
| static pthread_cond_t my_cond_command_is_running; | static pthread_cond_t my_cond_command_is_running; | ||||
| static bool my_stop_is_required = false; | static bool my_stop_is_required = false; | ||||
| static bool my_terminate_is_required = 0; | |||||
| // my_thread: reads commands from the fifo, and runs them. | // my_thread: reads commands from the fifo, and runs them. | ||||
| static pthread_t my_thread; | static pthread_t my_thread; | ||||
| bool look_for_inactivity = false; | bool look_for_inactivity = false; | ||||
| while (1) { | |||||
| while (!my_terminate_is_required) { | |||||
| bool a_start_is_required = false; | bool a_start_is_required = false; | ||||
| if (look_for_inactivity) { | if (look_for_inactivity) { | ||||
| a_start_is_required = sleep_until_start_request_or_inactivity(); | a_start_is_required = sleep_until_start_request_or_inactivity(); | ||||
| assert(!a_status); | assert(!a_status); | ||||
| if (!a_start_is_required) { | if (!a_start_is_required) { | ||||
| while (my_start_is_required == false) { | |||||
| while (my_start_is_required == false && my_terminate_is_required == false) { | |||||
| while ((pthread_cond_wait(&my_cond_start_is_required, &my_mutex) == -1) && errno == EINTR) | while ((pthread_cond_wait(&my_cond_start_is_required, &my_mutex) == -1) && errno == EINTR) | ||||
| continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
| } | } | ||||
| assert(-1 != pthread_cond_broadcast(&my_cond_command_is_running)); | assert(-1 != pthread_cond_broadcast(&my_cond_command_is_running)); | ||||
| assert(-1 != pthread_mutex_unlock(&my_mutex)); | assert(-1 != pthread_mutex_unlock(&my_mutex)); | ||||
| while (my_command_is_running) { | |||||
| while (my_command_is_running && !my_terminate_is_required) { | |||||
| int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
| assert(!a_status); | assert(!a_status); | ||||
| t_espeak_command *a_command = (t_espeak_command *)pop(); | t_espeak_command *a_command = (t_espeak_command *)pop(); | ||||
| } | } | ||||
| } | } | ||||
| if (my_stop_is_required) { | |||||
| if (my_stop_is_required || my_terminate_is_required) { | |||||
| // no mutex required since the stop command is synchronous | // no mutex required since the stop command is synchronous | ||||
| // and waiting for my_cond_stop_is_acknowledged | // and waiting for my_cond_stop_is_acknowledged | ||||
| init(1); | init(1); | ||||
| void fifo_terminate() | void fifo_terminate() | ||||
| { | { | ||||
| pthread_cancel(my_thread); | |||||
| my_terminate_is_required = true; | |||||
| pthread_cond_signal(&my_cond_start_is_required); | |||||
| pthread_join(my_thread, NULL); | pthread_join(my_thread, NULL); | ||||
| my_terminate_is_required = false; | |||||
| pthread_mutex_destroy(&my_mutex); | pthread_mutex_destroy(&my_mutex); | ||||
| pthread_cond_destroy(&my_cond_start_is_required); | pthread_cond_destroy(&my_cond_start_is_required); | ||||
| pthread_cond_destroy(&my_cond_stop_is_acknowledged); | pthread_cond_destroy(&my_cond_stop_is_acknowledged); |