From de9d5941b84bb36e54f1bb57416604f7e87c462e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Fri, 19 Mar 2021 13:54:35 -0400
Subject: [PATCH] Fix race condition when handle is opened by playing a sample.

Caused a SyncTERM hang if it played a FG beep.
---
 src/xpdev/xpbeep.c | 44 +++++++++++++++++++++++++++-----------------
 1 file changed, 27 insertions(+), 17 deletions(-)

diff --git a/src/xpdev/xpbeep.c b/src/xpdev/xpbeep.c
index 9693f7ba97..d1059f1ada 100644
--- a/src/xpdev/xpbeep.c
+++ b/src/xpdev/xpbeep.c
@@ -212,6 +212,7 @@ struct alsa_api_struct *alsa_api=NULL;
 
 #ifdef XPDEV_THREAD_SAFE
 static void init_sample(void);
+static BOOL xp_play_sample_locked(const unsigned char *sample, size_t size, BOOL background);
 #endif
 
 /********************************************************************************/
@@ -418,10 +419,12 @@ DLLCALL xptone_open_locked(void)
 			handle_rc++;
 #ifdef XPDEV_THREAD_SAFE
 			pthread_mutex_unlock(&handle_mutex);
-#endif
+			pthread_mutex_lock(&sample_mutex);
+			if (samples_posted == 0)
+				xp_play_sample_locked((unsigned char *)"\x80", 1, FALSE);
+			pthread_mutex_unlock(&sample_mutex);
+#else
 			xptone(0, 1, WAVE_SHAPE_SQUARE);
-#ifdef XPDEV_THREAD_SAFE
-			pthread_mutex_lock(&handle_mutex);
 #endif
 			if (pulseaudio_device_open_failed) {
 				handle_type = SOUND_DEVICE_CLOSED;
@@ -995,20 +998,20 @@ void DLLCALL xp_play_sample_thread(void *data)
 			waited=FALSE;
 		posted_last=FALSE;
 		pthread_mutex_lock(&handle_mutex);
-		if(pthread_mutex_lock(&sample_mutex)!=0) {
-			pthread_mutex_unlock(&handle_mutex);
-			goto error_return;
-		}
 
 		if(handle_type==SOUND_DEVICE_CLOSED) {
 			must_close=TRUE;
 			if(!xptone_open_locked()) {
 				sem_post(&sample_complete_sem);
-				pthread_mutex_unlock(&sample_mutex);
 				pthread_mutex_unlock(&handle_mutex);
 				continue;
 			}
 		}
+
+		if(pthread_mutex_lock(&sample_mutex)!=0) {
+			pthread_mutex_unlock(&handle_mutex);
+			goto error_return;
+		}
 		this_sample_size=sample_size;
 		FREE_AND_NULL(sample);
 		sample=(unsigned char *)malloc(sample_size);
@@ -1067,15 +1070,8 @@ init_sample(void)
 	sem_init(&sample_complete_sem, 0, 0);
 }
 
-/*
- * This MUST not return false after sample goes into the sample buffer in the background.
- * If it does, the caller won't be able to free() it.
- */
-BOOL DLLCALL xp_play_sample(const unsigned char *sample, size_t size, BOOL background)
+static BOOL xp_play_sample_locked(const unsigned char *sample, size_t size, BOOL background)
 {
-	pthread_once(&sample_initialized_pto, init_sample);
-
-	pthread_mutex_lock(&sample_mutex);
 	if(!sample_thread_running) {
 		_beginthread(xp_play_sample_thread, 0,NULL);
 		sem_wait(&sample_complete_sem);
@@ -1099,8 +1095,22 @@ BOOL DLLCALL xp_play_sample(const unsigned char *sample, size_t size, BOOL backg
 			samples_posted--;
 		}
 	}
+	return TRUE;
+}
+
+/*
+ * This MUST not return false after sample goes into the sample buffer in the background.
+ * If it does, the caller won't be able to free() it.
+ */
+BOOL DLLCALL xp_play_sample(const unsigned char *sample, size_t size, BOOL background)
+{
+	BOOL ret;
+	pthread_once(&sample_initialized_pto, init_sample);
+
+	pthread_mutex_lock(&sample_mutex);
+	ret = xp_play_sample_locked(sample, size, background);
 	pthread_mutex_unlock(&sample_mutex);
-	return(TRUE);
+	return(ret);
 }
 #else
 BOOL DLLCALL xp_play_sample(const unsigned char *sample, size_t sample_size, BOOL background)
-- 
GitLab