Browse Source

Avoid using pthread_cancel

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
Samuel Thibault 6 years ago
parent
commit
e13b602ce0
2 changed files with 19 additions and 10 deletions
  1. 10
    5
      src/libespeak-ng/event.c
  2. 9
    5
      src/libespeak-ng/fifo.c

+ 10
- 5
src/libespeak-ng/event.c View File

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);

+ 9
- 5
src/libespeak-ng/fifo.c View File

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);

Loading…
Cancel
Save