[alsa-devel] [PATCH 0/2] Fix thread safety issues

Takashi Iwai tiwai at suse.de
Thu Jan 31 09:54:56 CET 2013


At Wed, 30 Jan 2013 19:13:54 +0100,
Jérôme Forissier wrote:
> 
> 
> On 30 janv. 2013, at 17:03, Takashi Iwai wrote:
> 
> > At Wed, 30 Jan 2013 16:22:15 +0100,
> > Jerome Forissier wrote:
> >> 
> >> This is a set of two patches to fix thread safety issues I found when running
> >> a video application over ALSA. The application uses LibVLC to play several
> >> videos simultaneously. LibVLC calls snd_pcm_open() and snd_device_name_hint()
> >> from multiple threads, without locking--which is allowed according to
> >> http://www.alsa-project.org/main/index.php/SMP_Design.
> > 
> > The fix for snd_pcm_direct_parse_open_conf() looks good.  I'm going to
> > apply this now.
> 
> Good, thank you :)
> 
> > snd_device_name_hint() should use snd_config_update_r() and pass the
> > local config space to others instead of snd_config.
> 
> 
> I tried following this path, but there is a problem: try_config() calls snd_lib_error_set_handler() so I fail to see how we can make this code reentrant.
> In fact, the random errors I've seen when running my multi-threaded test were probably just a side-effect of fiddling with the global error handler from multiple threads. Probably harmless, but still it makes snd_device_name_hint() not thread-safe, strictly speaking.

Yeah, I see the issue now, too.

The problem is that the error handler is really global, not only used
in conf.c and co.  Thus applying a lock locally doesn't suffice.

> Any suggestion? Should I consider moving snd_lib_error to thread-local storage?

TLB would be an option, indeed.  But blindly moving snd_lib_error to
TLS breaks the current behavior, so we should avoid it.

How about a patch like below?  It adds another error redirection.
Unless local_error is set, the behavior is identical.

(Maybe we can name it better, yeah.  I just wanted to avoid to put a
 word "thread" in the function because it should be usable in both
 thread- and non-thread-capable environment.)


thanks,

Takashi

---
diff --git a/include/error.h b/include/error.h
index 6d27083..256cd5f 100644
--- a/include/error.h
+++ b/include/error.h
@@ -74,5 +74,11 @@ extern int snd_lib_error_set_handler(snd_lib_error_handler_t handler);
 }
 #endif
 
+typedef void (*snd_local_error_handler_t)(const char *file, int line,
+					  const char *func, int err,
+					  const char *fmt, va_list arg);
+
+snd_local_error_handler_t snd_lib_error_set_local(snd_local_error_handler_t func);
+
 #endif /* __ALSA_ERROR_H */
 
diff --git a/src/control/namehint.c b/src/control/namehint.c
index 19352be..defc036 100644
--- a/src/control/namehint.c
+++ b/src/control/namehint.c
@@ -80,7 +80,8 @@ static void zero_handler(const char *file ATTRIBUTE_UNUSED,
 			 int line ATTRIBUTE_UNUSED,
 			 const char *function ATTRIBUTE_UNUSED,
 			 int err ATTRIBUTE_UNUSED,
-			 const char *fmt ATTRIBUTE_UNUSED, ...)
+			 const char *fmt ATTRIBUTE_UNUSED,
+			 va_list arg ATTRIBUTE_UNUSED)
 {
 }
 
@@ -212,7 +213,7 @@ static int try_config(struct hint_list *list,
 		      const char *base,
 		      const char *name)
 {
-	snd_lib_error_handler_t eh;
+	snd_local_error_handler_t eh;
 	snd_config_t *res = NULL, *cfg, *cfg1, *n;
 	snd_config_iterator_t i, next;
 	char *buf, *buf1 = NULL, *buf2;
@@ -239,10 +240,9 @@ static int try_config(struct hint_list *list,
 		sprintf(buf, "%s:CARD=%s", name, snd_ctl_card_info_get_id(list->info));
 	else
 		strcpy(buf, name);
-	eh = snd_lib_error;
-	snd_lib_error_set_handler(&zero_handler);
+	eh = snd_lib_error_set_local(&zero_handler);
 	err = snd_config_search_definition(snd_config, base, buf, &res);
-	snd_lib_error_set_handler(eh);
+	snd_lib_error_set_local(eh);
 	if (err < 0)
 		goto __skip_add;
 	cleanup_res = 1;
@@ -337,10 +337,9 @@ static int try_config(struct hint_list *list,
 		goto __ok;
 	/* find, if all parameters have a default, */
 	/* otherwise filter this definition */
-	eh = snd_lib_error;
-	snd_lib_error_set_handler(&zero_handler);
+	eh = snd_lib_error_set_local(&zero_handler);
 	err = snd_config_search_alias_hooks(snd_config, base, buf, &res);
-	snd_lib_error_set_handler(eh);
+	snd_lib_error_set_local(eh);
 	if (err < 0)
 		goto __cleanup;
 	if (snd_config_search(res, "@args", &cfg) >= 0) {
diff --git a/src/error.c b/src/error.c
index 7d5f509..6026ede 100644
--- a/src/error.c
+++ b/src/error.c
@@ -60,6 +60,23 @@ const char *snd_strerror(int errnum)
 	return snd_error_codes[errnum];
 }
 
+#define SUPPORT_TLS /* XXX should be checked in configure */
+
+#ifdef SUPPORT_TLS
+#define TLS_PFX		__thread
+#else
+#define TLS_PFX		/* NOP */
+#endif
+
+static TLS_PFX snd_local_error_handler_t local_error;
+
+snd_local_error_handler_t snd_lib_error_set_local(snd_local_error_handler_t func)
+{
+	snd_local_error_handler_t old = local_error;
+	local_error = func;
+	return old;
+}
+
 /**
  * \brief The default error handler function.
  * \param file The filename where the error was hit.
@@ -75,6 +92,11 @@ static void snd_lib_error_default(const char *file, int line, const char *functi
 {
 	va_list arg;
 	va_start(arg, fmt);
+	if (local_error) {
+		local_error(file, line, function, err, fmt, arg);
+		va_end(arg);
+		return;
+	}
 	fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
 	vfprintf(stderr, fmt, arg);
 	if (err)


More information about the Alsa-devel mailing list