From c4839c04b634316265365686ab5bcca9ff579cc4 Mon Sep 17 00:00:00 2001
From: Andre Barbosa <andre.emanuel.barbosa@gmail.com>
Date: Thu, 5 Aug 2021 17:55:33 +0100
Subject: [PATCH] media_cache: Don't lock when curl the remote file

When playing a remote sound file, which is not in cache, first we need
to download it with ast_bucket_file_retrieve.

This can take a while if the remote host is slow. The current CURL
timeout is 180secs, so in extreme situations, it can take 3 minutes to
return.

Because ast_media_cache_retrieve has a lock on all function, while we
are waiting for the delayed download, Asterisk is not able to play any
more files, even the files already cached locally.

ASTERISK-29544 #close

Change-Id: I8d4142b463ae4a1d4c41bff2bf63324821567408
---
 main/media_cache.c | 27 +++++++++++++++++++++++++--
 1 file changed, 25 insertions(+), 2 deletions(-)

diff --git a/main/media_cache.c b/main/media_cache.c
index 1899fb453f..7e1cbe3762 100644
--- a/main/media_cache.c
+++ b/main/media_cache.c
@@ -157,13 +157,15 @@ int ast_media_cache_retrieve(const char *uri, const char *preferred_file_name,
 	char *file_path, size_t len)
 {
 	struct ast_bucket_file *bucket_file;
+	struct ast_bucket_file *tmp_bucket_file;
 	char *ext;
-	SCOPED_AO2LOCK(media_lock, media_cache);
-
 	if (ast_strlen_zero(uri)) {
 		return -1;
 	}
 
+	ao2_lock(media_cache);
+	ast_debug(5, "Looking for media at local cache, file: %s\n", uri);
+
 	/* First, retrieve from the ao2 cache here. If we find a bucket_file
 	 * matching the requested URI, ask the appropriate backend if it is
 	 * stale. If not; return it.
@@ -179,6 +181,7 @@ int ast_media_cache_retrieve(const char *uri, const char *preferred_file_name,
 			ao2_ref(bucket_file, -1);
 
 			ast_debug(5, "Returning media at local file: %s\n", file_path);
+			ao2_unlock(media_cache);
 			return 0;
 		}
 
@@ -187,6 +190,10 @@ int ast_media_cache_retrieve(const char *uri, const char *preferred_file_name,
 		ast_bucket_file_delete(bucket_file);
 		ao2_ref(bucket_file, -1);
 	}
+	/* We unlock to retrieve the file, because it can take a long time;
+	 * and we don't want to lock access to cached files while waiting
+	 */
+	ao2_unlock(media_cache);
 
 	/* Either this is new or the resource is stale; do a full retrieve
 	 * from the appropriate bucket_file backend
@@ -197,6 +204,21 @@ int ast_media_cache_retrieve(const char *uri, const char *preferred_file_name,
 		return -1;
 	}
 
+	/* we lock again, before updating cache */
+	ao2_lock(media_cache);
+
+	/* We can have duplicated buckets here, we check if already exists
+	 * before saving
+	 */
+	tmp_bucket_file = ao2_find(media_cache, uri, OBJ_SEARCH_KEY | OBJ_NOLOCK);
+	if (tmp_bucket_file) {
+		ao2_ref(tmp_bucket_file, -1);
+		ast_bucket_file_delete(bucket_file);
+		ao2_ref(bucket_file, -1);
+		ao2_unlock(media_cache);
+		return 0;
+	}
+
 	/* We can manipulate the 'immutable' bucket_file here, as we haven't
 	 * let anyone know of its existence yet
 	 */
@@ -210,6 +232,7 @@ int ast_media_cache_retrieve(const char *uri, const char *preferred_file_name,
 	ao2_ref(bucket_file, -1);
 
 	ast_debug(5, "Returning media at local file: %s\n", file_path);
+	ao2_unlock(media_cache);
 
 	return 0;
 }
-- 
GitLab