| #include <assert.h> | #include <assert.h> | ||||
| #include <errno.h> | #include <errno.h> | ||||
| #include <pthread.h> | #include <pthread.h> | ||||
| #include <semaphore.h> | |||||
| #include <stdbool.h> | #include <stdbool.h> | ||||
| #include <stdint.h> | #include <stdint.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| // my_mutex: protects my_thread_is_talking, | // my_mutex: protects my_thread_is_talking, | ||||
| static pthread_mutex_t my_mutex; | static pthread_mutex_t my_mutex; | ||||
| static sem_t my_sem_start_is_required; | |||||
| static sem_t my_sem_stop_is_required; | |||||
| static sem_t my_sem_stop_is_acknowledged; | |||||
| static pthread_cond_t my_cond_start_is_required; | |||||
| static int my_start_is_required = 0; | |||||
| static pthread_cond_t my_cond_stop_is_required; | |||||
| static int my_stop_is_required = 0; | |||||
| static pthread_cond_t my_cond_stop_is_acknowledged; | |||||
| static int my_stop_is_acknowledged = 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; | ||||
| pthread_mutex_init(&my_mutex, (const pthread_mutexattr_t *)NULL); | pthread_mutex_init(&my_mutex, (const pthread_mutexattr_t *)NULL); | ||||
| init(); | init(); | ||||
| assert(-1 != sem_init(&my_sem_start_is_required, 0, 0)); | |||||
| assert(-1 != sem_init(&my_sem_stop_is_required, 0, 0)); | |||||
| assert(-1 != sem_init(&my_sem_stop_is_acknowledged, 0, 0)); | |||||
| assert(-1 != pthread_cond_init(&my_cond_start_is_required, NULL)); | |||||
| assert(-1 != pthread_cond_init(&my_cond_stop_is_required, NULL)); | |||||
| assert(-1 != pthread_cond_init(&my_cond_stop_is_acknowledged, NULL)); | |||||
| pthread_attr_t a_attrib; | pthread_attr_t a_attrib; | ||||
| espeak_ng_STATUS status; | espeak_ng_STATUS status; | ||||
| if ((status = pthread_mutex_lock(&my_mutex)) != ENS_OK) { | if ((status = pthread_mutex_lock(&my_mutex)) != ENS_OK) { | ||||
| sem_post(&my_sem_start_is_required); | |||||
| my_start_is_required = 1; | |||||
| return status; | return status; | ||||
| } | } | ||||
| if ((status = push(a_event)) != ENS_OK) { | if ((status = push(a_event)) != ENS_OK) { | ||||
| event_delete(a_event); | event_delete(a_event); | ||||
| pthread_mutex_unlock(&my_mutex); | pthread_mutex_unlock(&my_mutex); | ||||
| } else | |||||
| } else { | |||||
| my_start_is_required = 1; | |||||
| pthread_cond_signal(&my_cond_start_is_required); | |||||
| status = pthread_mutex_unlock(&my_mutex); | status = pthread_mutex_unlock(&my_mutex); | ||||
| } | |||||
| sem_post(&my_sem_start_is_required); | |||||
| return status; | return status; | ||||
| } | } | ||||
| int a_event_is_running = 0; | int a_event_is_running = 0; | ||||
| if (my_event_is_running) { | if (my_event_is_running) { | ||||
| sem_post(&my_sem_stop_is_required); | |||||
| my_stop_is_required = 1; | |||||
| pthread_cond_signal(&my_cond_stop_is_required); | |||||
| a_event_is_running = 1; | a_event_is_running = 1; | ||||
| } else | } else | ||||
| init(); // clear pending events | init(); // clear pending events | ||||
| return status; | return status; | ||||
| if (a_event_is_running) { | if (a_event_is_running) { | ||||
| while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) | |||||
| while(my_stop_is_acknowledged == 0) | |||||
| while((pthread_cond_wait(&my_cond_stop_is_acknowledged, &my_mutex) == -1) && errno == EINTR) | |||||
| continue; // Restart when interrupted by handler | continue; // Restart when interrupted by handler | ||||
| } | } | ||||
| int a_status = pthread_mutex_lock(&my_mutex); | int a_status = pthread_mutex_lock(&my_mutex); | ||||
| my_event_is_running = 0; | my_event_is_running = 0; | ||||
| pthread_mutex_unlock(&my_mutex); | |||||
| while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) | |||||
| while(my_start_is_required == 0) | |||||
| 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 | ||||
| a_status = pthread_mutex_lock(&my_mutex); | |||||
| my_event_is_running = 1; | my_event_is_running = 1; | ||||
| pthread_mutex_unlock(&my_mutex); | |||||
| a_stop_is_required = 0; | a_stop_is_required = 0; | ||||
| a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); // NOTE: may set a_stop_is_required to -1 | |||||
| if ((a_status == 0) && (a_stop_is_required > 0)) { | |||||
| while (0 == sem_trywait(&my_sem_stop_is_required)) | |||||
| ; | |||||
| } else | |||||
| a_stop_is_required = 0; | |||||
| my_start_is_required = 0; | |||||
| pthread_mutex_unlock(&my_mutex); | |||||
| // In this loop, my_event_is_running = 1 | // In this loop, my_event_is_running = 1 | ||||
| while (head && (a_stop_is_required <= 0)) { | |||||
| while (0 == sem_trywait(&my_sem_start_is_required)) | |||||
| ; | |||||
| while (head && (a_stop_is_required == 0)) { | |||||
| espeak_EVENT *event = (espeak_EVENT *)(head->data); | espeak_EVENT *event = (espeak_EVENT *)(head->data); | ||||
| assert(event); | assert(event); | ||||
| a_status = pthread_mutex_lock(&my_mutex); | a_status = pthread_mutex_lock(&my_mutex); | ||||
| event_delete((espeak_EVENT *)pop()); | event_delete((espeak_EVENT *)pop()); | ||||
| a_status = pthread_mutex_unlock(&my_mutex); | |||||
| a_stop_is_required = my_stop_is_required; | |||||
| if(a_stop_is_required > 0) | |||||
| my_stop_is_required = 0; | |||||
| a_stop_is_required = 0; | |||||
| a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); | |||||
| if ((a_status == 0) && (a_stop_is_required > 0)) { | |||||
| while (0 == sem_trywait(&my_sem_stop_is_required)) | |||||
| ; | |||||
| } else | |||||
| a_stop_is_required = 0; | |||||
| a_status = pthread_mutex_unlock(&my_mutex); | |||||
| } | } | ||||
| a_status = pthread_mutex_lock(&my_mutex); | a_status = pthread_mutex_lock(&my_mutex); | ||||
| my_event_is_running = 0; | my_event_is_running = 0; | ||||
| if (a_stop_is_required <= 0) { | |||||
| a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); | |||||
| if ((a_status == 0) && (a_stop_is_required > 0)) { | |||||
| while (0 == sem_trywait(&my_sem_stop_is_required)) | |||||
| ; | |||||
| } else | |||||
| a_stop_is_required = 0; | |||||
| if (a_stop_is_required == 0) { | |||||
| a_stop_is_required = my_stop_is_required; | |||||
| if (a_stop_is_required > 0) | |||||
| my_stop_is_required = 0; | |||||
| } | } | ||||
| a_status = pthread_mutex_unlock(&my_mutex); | a_status = pthread_mutex_unlock(&my_mutex); | ||||
| if (a_stop_is_required > 0) { | if (a_stop_is_required > 0) { | ||||
| // no mutex required since the stop command is synchronous | // no mutex required since the stop command is synchronous | ||||
| // and waiting for my_sem_stop_is_acknowledged | |||||
| // and waiting for my_cond_stop_is_acknowledged | |||||
| init(); | init(); | ||||
| // acknowledge the stop request | // acknowledge the stop request | ||||
| a_status = sem_post(&my_sem_stop_is_acknowledged); | |||||
| espeak_ng_STATUS a_status = pthread_mutex_lock(&my_mutex); | |||||
| my_stop_is_acknowledged = 1; | |||||
| a_status = pthread_cond_signal(&my_cond_stop_is_acknowledged); | |||||
| a_status = pthread_mutex_unlock(&my_mutex); | |||||
| } | } | ||||
| } | } | ||||
| pthread_cancel(my_thread); | pthread_cancel(my_thread); | ||||
| pthread_join(my_thread, NULL); | pthread_join(my_thread, NULL); | ||||
| pthread_mutex_destroy(&my_mutex); | pthread_mutex_destroy(&my_mutex); | ||||
| sem_destroy(&my_sem_start_is_required); | |||||
| sem_destroy(&my_sem_stop_is_required); | |||||
| sem_destroy(&my_sem_stop_is_acknowledged); | |||||
| pthread_cond_destroy(&my_cond_start_is_required); | |||||
| pthread_cond_destroy(&my_cond_stop_is_required); | |||||
| pthread_cond_destroy(&my_cond_stop_is_acknowledged); | |||||
| init(); // purge event | init(); // purge event | ||||
| thread_inited = 0; | thread_inited = 0; | ||||
| } | } |