[asterisk-commits] russell: branch 1.6.0 r166276 - in /branches/1.6.0: ./ res/res_musiconhold.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Dec 22 10:17:58 CST 2008


Author: russell
Date: Mon Dec 22 10:17:57 2008
New Revision: 166276

URL: http://svn.digium.com/view/asterisk?view=rev&rev=166276
Log:
Merged revisions 166273 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/trunk

........
r166273 | russell | 2008-12-22 10:10:40 -0600 (Mon, 22 Dec 2008) | 7 lines

Re-work ref count handling of MoH classes using astobj2 to resolve crashes.

(closes issue #13566)
Reported by: igorcarneiro
Tested by: russell
Review: http://reviewboard.digium.com/r/106/

........

Modified:
    branches/1.6.0/   (props changed)
    branches/1.6.0/res/res_musiconhold.c

Propchange: branches/1.6.0/
------------------------------------------------------------------------------
Binary property 'trunk-merged' - no diff available.

Modified: branches/1.6.0/res/res_musiconhold.c
URL: http://svn.digium.com/view/asterisk/branches/1.6.0/res/res_musiconhold.c?view=diff&rev=166276&r1=166275&r2=166276
==============================================================================
--- branches/1.6.0/res/res_musiconhold.c (original)
+++ branches/1.6.0/res/res_musiconhold.c Mon Dec 22 10:17:57 2008
@@ -46,7 +46,7 @@
 #include <thread.h>
 #endif
 
-#if defined(HAVE_DAHDI)
+#ifdef HAVE_DAHDI
 #include <dahdi/user.h>
 #endif
 
@@ -64,6 +64,7 @@
 #include "asterisk/cli.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/linkedlists.h"
+#include "asterisk/astobj2.h"
 
 #define INITIAL_NUM_FILES   8
 
@@ -160,8 +161,6 @@
 	int srcfd;
 	/*! FD for timing source */
 	int pseudofd;
-	/*! Number of users */
-	int inuse;
 	/*! Created on the fly, from RT engine */
 	int realtime;
 	unsigned int delete:1;
@@ -177,63 +176,44 @@
 	AST_LIST_ENTRY(mohdata) list;
 };
 
-AST_RWLIST_HEAD_STATIC(mohclasses, mohclass);
+static struct ao2_container *mohclasses;
 
 #define LOCAL_MPG_123 "/usr/local/bin/mpg123"
 #define MPG_123 "/usr/bin/mpg123"
 #define MAX_MP3S 256
 
-static int ast_moh_destroy_one(struct mohclass *moh);
 static int reload(void);
 
-static void ast_moh_free_class(struct mohclass **mohclass) 
-{
-	struct mohdata *member;
-	struct mohclass *class = *mohclass;
-	int i;
+#define mohclass_ref(class)   (ao2_ref((class), +1), class)
+#define mohclass_unref(class) (ao2_ref((class), -1), NULL)
+
+static void moh_files_release(struct ast_channel *chan, void *data)
+{
+	struct moh_files_state *state;
+
+	if (!chan || !chan->music_state) {
+		return;
+	}
+
+	state = chan->music_state;
+
+	if (chan->stream) {
+		ast_closestream(chan->stream);
+		chan->stream = NULL;
+	}
 	
-	while ((member = AST_LIST_REMOVE_HEAD(&class->members, list)))
-		ast_free(member);
-	
-	if (class->thread) {
-		pthread_cancel(class->thread);
-		class->thread = 0;
-	}
-
-	if (class->filearray) {
-		for (i = 0; i < class->total_files; i++)
-			ast_free(class->filearray[i]);
-		ast_free(class->filearray);
-	}
-
-	ast_free(class);
-	*mohclass = NULL;
-}
-
-
-static void moh_files_release(struct ast_channel *chan, void *data)
-{
-	struct moh_files_state *state;
-
-	if (chan) {
-		if ((state = chan->music_state)) {
-			if (chan->stream) {
-	                        ast_closestream(chan->stream);
-	                        chan->stream = NULL;
-	                }
-			ast_verb(3, "Stopped music on hold on %s\n", chan->name);
-	
-			if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
-				ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
-			}
-			state->save_pos = state->pos;
-
-			if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
-				ast_moh_destroy_one(state->class);
-		}
-	}
-}
-
+	if (option_verbose > 2) {
+		ast_verbose(VERBOSE_PREFIX_3 "Stopped music on hold on %s\n", chan->name);
+	}
+
+	if (state->origwfmt && ast_set_write_format(chan, state->origwfmt)) {
+		ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, state->origwfmt);
+	}
+
+	state->save_pos = state->pos;
+
+	state->class = mohclass_unref(state->class);
+}
 
 static int ast_moh_files_next(struct ast_channel *chan) 
 {
@@ -289,7 +269,6 @@
 
 	return 0;
 }
-
 
 static struct ast_frame *moh_files_readframe(struct ast_channel *chan) 
 {
@@ -327,7 +306,6 @@
 	return res;
 }
 
-
 static void *moh_files_alloc(struct ast_channel *chan, void *params)
 {
 	struct moh_files_state *state;
@@ -335,53 +313,62 @@
 
 	if (!chan->music_state && (state = ast_calloc(1, sizeof(*state)))) {
 		chan->music_state = state;
-		state->class = class;
+		state->class = mohclass_ref(class);
 		state->save_pos = -1;
-	} else 
+	} else {
 		state = chan->music_state;
-
-	if (state) {
-		if (state->class != class) {
-			/* initialize */
-			memset(state, 0, sizeof(*state));
-			state->class = class;
-			if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files)
-				state->pos = ast_random() % class->total_files;
-		}
-
-		state->origwfmt = chan->writeformat;
-
-		ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
-	}
+	}
+
+	if (!state) {
+		return NULL;
+	}
+
+	if (state->class != class) {
+		/* (re-)initialize */
+		if (state->class) {
+			state->class = mohclass_unref(state->class);
+		}
+		memset(state, 0, sizeof(*state));
+		state->class = mohclass_ref(class);
+		if (ast_test_flag(state->class, MOH_RANDOMIZE) && class->total_files) {
+			state->pos = ast_random() % class->total_files;
+		}
+	}
+
+	state->origwfmt = chan->writeformat;
+
+	ast_verb(3, "Started music on hold, class '%s', on %s\n", class->name, chan->name);
 	
 	return chan->music_state;
 }
 
+static int moh_digit_match(void *obj, void *arg, int flags)
+{
+	char *digit = arg;
+	struct mohclass *class = obj;
+
+	return (*digit == class->digit) ? CMP_MATCH | CMP_STOP : 0;
+}
+
 /*! \note This function should be called with the mohclasses list locked */
 static struct mohclass *get_mohbydigit(char digit)
 {
-	struct mohclass *moh = NULL;
-
-	AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
-		if (digit == moh->digit)
-			break;
-	}
-
-	return moh;
+	return ao2_callback(mohclasses, 0, moh_digit_match, &digit);
 }
 
 static void moh_handle_digit(struct ast_channel *chan, char digit)
 {
-	struct mohclass *moh;
+	struct mohclass *class;
 	const char *classname = NULL;
 
-	AST_RWLIST_RDLOCK(&mohclasses);
-	if ((moh = get_mohbydigit(digit)))
-		classname = ast_strdupa(moh->name);
-	AST_RWLIST_UNLOCK(&mohclasses);
-
-	if (!moh)
+	if ((class = get_mohbydigit(digit))) {
+		classname = ast_strdupa(class->name);
+		class = mohclass_unref(class);
+	}
+
+	if (!class) {
 		return;
+	}
 
 	ast_moh_stop(chan);
 	ast_moh_start(chan, classname, NULL);
@@ -389,10 +376,10 @@
 
 static struct ast_generator moh_file_stream = 
 {
-	alloc: moh_files_alloc,
-	release: moh_files_release,
-	generate: moh_files_generator,
-	digit: moh_handle_digit,
+	.alloc    = moh_files_alloc,
+	.release  = moh_files_release,
+	.generate = moh_files_generator,
+	.digit    = moh_handle_digit,
 };
 
 static int spawn_mp3(struct mohclass *class)
@@ -621,15 +608,17 @@
 			}
 			continue;
 		}
+
 		pthread_testcancel();
-		AST_RWLIST_RDLOCK(&mohclasses);
-		AST_RWLIST_TRAVERSE(&class->members, moh, list) {
+
+		ao2_lock(class);
+		AST_LIST_TRAVERSE(&class->members, moh, list) {
 			/* Write data */
 			if ((res = write(moh->pipe[1], sbuf, res2)) != res2) {
 				ast_debug(1, "Only wrote %d of %d bytes to pipe\n", res, res2);
 			}
 		}
-		AST_RWLIST_UNLOCK(&mohclasses);
+		ao2_unlock(class);
 	}
 	return NULL;
 }
@@ -740,18 +729,20 @@
 	return 0;
 }
 
-/*! \note This function should be called with the mohclasses list locked */
 static struct mohclass *get_mohbyname(const char *name, int warn)
 {
 	struct mohclass *moh = NULL;
-
-	AST_RWLIST_TRAVERSE(&mohclasses, moh, list) {
-		if (!strcasecmp(name, moh->name))
-			break;
-	}
-
-	if (!moh && warn)
+	struct mohclass tmp_class = {
+		.flags = 0,
+	};
+
+	ast_copy_string(tmp_class.name, name, sizeof(tmp_class.name));
+
+	moh = ao2_find(mohclasses, &tmp_class, 0);
+
+	if (!moh && warn) {
 		ast_log(LOG_DEBUG, "Music on Hold class '%s' not found in memory\n", name);
+	}
 
 	return moh;
 }
@@ -780,11 +771,11 @@
 	moh->f.subclass = cl->format;
 	moh->f.offset = AST_FRIENDLY_OFFSET;
 
-	moh->parent = cl;
-
-	AST_RWLIST_WRLOCK(&mohclasses);
+	moh->parent = mohclass_ref(cl);
+
+	ao2_lock(cl);
 	AST_LIST_INSERT_HEAD(&cl->members, moh, list);
-	AST_RWLIST_UNLOCK(&mohclasses);
+	ao2_unlock(cl);
 	
 	return moh;
 }
@@ -792,26 +783,28 @@
 static void moh_release(struct ast_channel *chan, void *data)
 {
 	struct mohdata *moh = data;
+	struct mohclass *class = moh->parent;
 	int oldwfmt;
-	struct moh_files_state *state;
-
-	AST_RWLIST_WRLOCK(&mohclasses);
-	AST_RWLIST_REMOVE(&moh->parent->members, moh, list);	
-	AST_RWLIST_UNLOCK(&mohclasses);
+
+	ao2_lock(class);
+	AST_LIST_REMOVE(&moh->parent->members, moh, list);	
+	ao2_unlock(class);
 	
 	close(moh->pipe[0]);
 	close(moh->pipe[1]);
+
 	oldwfmt = moh->origwfmt;
-	state = chan->music_state;
-	if (moh->parent->delete && ast_atomic_dec_and_test(&moh->parent->inuse))
-		ast_moh_destroy_one(moh->parent);
-	if (ast_atomic_dec_and_test(&state->class->inuse) && state->class->delete)
-		ast_moh_destroy_one(state->class);
+
+	moh->parent = class = mohclass_unref(class);
 
 	ast_free(moh);
+
 	if (chan) {
-		if (oldwfmt && ast_set_write_format(chan, oldwfmt)) 
-			ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n", chan->name, ast_getformatname(oldwfmt));
+		if (oldwfmt && ast_set_write_format(chan, oldwfmt)) {
+			ast_log(LOG_WARNING, "Unable to restore channel '%s' to format %s\n",
+					chan->name, ast_getformatname(oldwfmt));
+		}
+
 		ast_verb(3, "Stopped music on hold on %s\n", chan->name);
 	}
 }
@@ -851,10 +844,6 @@
 	short buf[1280 + AST_FRIENDLY_OFFSET / 2];
 	int res;
 
-	if (!moh->parent->pid && moh->parent->inuse == 0) {
-		return -1;
-	}
-
 	len = ast_codec_get_len(moh->parent->format, samples);
 
 	if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
@@ -877,11 +866,10 @@
 	return 0;
 }
 
-static struct ast_generator mohgen = 
-{
-	alloc: moh_alloc,
-	release: moh_release,
-	generate: moh_generate,
+static struct ast_generator mohgen = {
+	.alloc    = moh_alloc,
+	.release  = moh_release,
+	.generate = moh_generate,
 	digit: moh_handle_digit
 };
 
@@ -993,98 +981,135 @@
 	return class->total_files;
 }
 
+static int init_files_class(struct mohclass *class)
+{
+	int res;
+
+	res = moh_scan_files(class);
+
+	if (res < 0) {
+		return -1;
+	}
+
+	if (!res) {
+		if (option_verbose > 2) {
+			ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n",
+					class->dir, class->name);
+		}
+		return -1;
+	}
+
+	if (strchr(class->args, 'r')) {
+		ast_set_flag(class, MOH_RANDOMIZE);
+	}
+
+	return 0;
+}
+
+
 static int moh_diff(struct mohclass *old, struct mohclass *new)
 {
-	if (!old || !new)
-		return -1;
-
-	if (strcmp(old->dir, new->dir))
-		return -1;
-	else if (strcmp(old->mode, new->mode))
-		return -1;
-	else if (strcmp(old->args, new->args))
-		return -1;
-	else if (old->flags != new->flags)
-		return -1;
+	if (!old || !new) {
+		return -1;
+	}
+
+	if (strcmp(old->dir, new->dir)) {
+		return -1;
+	} else if (strcmp(old->mode, new->mode)) {
+		return -1;
+	} else if (strcmp(old->args, new->args)) {
+		return -1;
+	} else if (old->flags != new->flags) {
+		return -1;
+	}
 
 	return 0;
 }
 
-static int moh_register(struct mohclass *moh, int reload)
+static int init_app_class(struct mohclass *class)
 {
 #ifdef HAVE_DAHDI
 	int x;
 #endif
+
+	if (!strcasecmp(class->mode, "custom")) {
+		ast_set_flag(class, MOH_CUSTOM);
+	} else if (!strcasecmp(class->mode, "mp3nb")) {
+		ast_set_flag(class, MOH_SINGLE);
+	} else if (!strcasecmp(class->mode, "quietmp3nb")) {
+		ast_set_flag(class, MOH_SINGLE | MOH_QUIET);
+	} else if (!strcasecmp(class->mode, "quietmp3")) {
+		ast_set_flag(class, MOH_QUIET);
+	}
+		
+	class->srcfd = -1;
+	class->pseudofd = -1;
+
+#ifdef HAVE_DAHDI
+	/* Open /dev/zap/pseudo for timing...  Is
+	   there a better, yet reliable way to do this? */
+	class->pseudofd = open("/dev/dahdi/psuedo", O_RDONLY);
+	if (class->pseudofd < 0) {
+		ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
+	} else {
+		x = 320;
+		ioctl(class->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
+	}
+#endif
+
+	if (ast_pthread_create_background(&class->thread, NULL, monmp3thread, class)) {
+		ast_log(LOG_WARNING, "Unable to create moh thread...\n");
+		if (class->pseudofd > -1) {
+			close(class->pseudofd);
+			class->pseudofd = -1;
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+/*!
+ * \note This function owns the reference it gets to moh
+ */
+static int moh_register(struct mohclass *moh, int reload)
+{
 	struct mohclass *mohclass = NULL;
-	int res = 0;
-
-	AST_RWLIST_WRLOCK(&mohclasses);
+
 	if ((mohclass = get_mohbyname(moh->name, 0)) && !moh_diff(mohclass, moh)) {
 		if (!mohclass->delete) {
-			ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
-			ast_free(moh);
-			AST_RWLIST_UNLOCK(&mohclasses);
+ 			ast_log(LOG_WARNING, "Music on Hold class '%s' already exists\n", moh->name);
+			mohclass = mohclass_unref(mohclass);
+			moh = mohclass_unref(moh);
 			return -1;
 		}
-	}
-	AST_RWLIST_UNLOCK(&mohclasses);
+		mohclass = mohclass_unref(mohclass);
+	}
 
 	time(&moh->start);
 	moh->start -= respawn_time;
 	
 	if (!strcasecmp(moh->mode, "files")) {
-		res = moh_scan_files(moh);
-		if (res <= 0) {
-			if (res == 0) {
-				if (option_verbose > 2)
-					ast_verbose(VERBOSE_PREFIX_3 "Files not found in %s for moh class:%s\n", moh->dir, moh->name);
-			}
-			ast_moh_free_class(&moh);
+		if (init_files_class(moh)) {
+			moh = mohclass_unref(moh);
 			return -1;
 		}
-		if (strchr(moh->args, 'r'))
-			ast_set_flag(moh, MOH_RANDOMIZE);
-	} else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || !strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || !strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
-
-		if (!strcasecmp(moh->mode, "custom"))
-			ast_set_flag(moh, MOH_CUSTOM);
-		else if (!strcasecmp(moh->mode, "mp3nb"))
-			ast_set_flag(moh, MOH_SINGLE);
-		else if (!strcasecmp(moh->mode, "quietmp3nb"))
-			ast_set_flag(moh, MOH_SINGLE | MOH_QUIET);
-		else if (!strcasecmp(moh->mode, "quietmp3"))
-			ast_set_flag(moh, MOH_QUIET);
-		
-		moh->srcfd = -1;
-#ifdef HAVE_DAHDI
-		/* Open /dev/dahdi/pseudo for timing...  Is
-		   there a better, yet reliable way to do this? */
-		moh->pseudofd = open("/dev/dahdi/pseudo", O_RDONLY);
-		if (moh->pseudofd < 0) {
-			ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
-		} else {
-			x = 320;
-			ioctl(moh->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
-		}
-#else
-		moh->pseudofd = -1;
-#endif
-		if (ast_pthread_create_background(&moh->thread, NULL, monmp3thread, moh)) {
-			ast_log(LOG_WARNING, "Unable to create moh...\n");
-			if (moh->pseudofd > -1)
-				close(moh->pseudofd);
-			ast_moh_free_class(&moh);
+	} else if (!strcasecmp(moh->mode, "mp3") || !strcasecmp(moh->mode, "mp3nb") || 
+			!strcasecmp(moh->mode, "quietmp3") || !strcasecmp(moh->mode, "quietmp3nb") || 
+			!strcasecmp(moh->mode, "httpmp3") || !strcasecmp(moh->mode, "custom")) {
+		if (init_app_class(moh)) {
+			moh = mohclass_unref(moh);
 			return -1;
 		}
 	} else {
 		ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", moh->mode);
-		ast_moh_free_class(&moh);
-		return -1;
-	}
-
-	AST_RWLIST_WRLOCK(&mohclasses);
-	AST_RWLIST_INSERT_HEAD(&mohclasses, moh, list);
-	AST_RWLIST_UNLOCK(&mohclasses);
+		moh = mohclass_unref(moh);
+		return -1;
+	}
+
+	ao2_link(mohclasses, moh);
+
+	moh = mohclass_unref(moh);
 	
 	return 0;
 }
@@ -1094,34 +1119,19 @@
 	struct moh_files_state *state = chan->music_state;
 
 	if (state) {
-		if (state->class->realtime) {
-			if (ast_test_flag(global_flags, MOH_CACHERTCLASSES)) {
-				/* We are cleaning out cached RT class, we should remove it from list, if no one else using it */
-				if (!(state->class->inuse)) {
-					/* Remove this class from list */
-					AST_RWLIST_WRLOCK(&mohclasses);
-					AST_RWLIST_REMOVE(&mohclasses, state->class, list);
-					AST_RWLIST_UNLOCK(&mohclasses);
-	
-					/* Free some memory */
-					ast_moh_destroy_one(state->class);
-				}
-			} else {
-				ast_moh_destroy_one(state->class);
-			}
-		}
 		ast_free(chan->music_state);
 		chan->music_state = NULL;
 	}
 }
 
+static void moh_class_destructor(void *obj);
+
 static struct mohclass *moh_class_malloc(void)
 {
 	struct mohclass *class;
 
-	if ((class = ast_calloc(1, sizeof(*class)))) {
+	if ((class = ao2_alloc(sizeof(*class), moh_class_destructor))) {
 		class->format = AST_FORMAT_SLINEAR;
-		class->realtime = 0;
 	}
 
 	return class;
@@ -1130,12 +1140,8 @@
 static int local_ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
 {
 	struct mohclass *mohclass = NULL;
-	struct ast_variable *var = NULL;
-	struct ast_variable *tmp = NULL;
 	struct moh_files_state *state = chan->music_state;
-#ifdef HAVE_DAHDI
-	int x;
-#endif
+	int res;
 
 	/* The following is the order of preference for which class to use:
 	 * 1) The channels explicitly set musicclass, which should *only* be
@@ -1148,19 +1154,20 @@
 	 *    option.
 	 * 4) The default class.
 	 */
-	
-	/* First, let's check in memory for static and cached RT classes */
-	AST_RWLIST_RDLOCK(&mohclasses);
-	if (!ast_strlen_zero(chan->musicclass))
+	if (!ast_strlen_zero(chan->musicclass)) {
 		mohclass = get_mohbyname(chan->musicclass, 1);
-	if (!mohclass && !ast_strlen_zero(mclass))
+	}
+	if (!mohclass && !ast_strlen_zero(mclass)) {
 		mohclass = get_mohbyname(mclass, 1);
-	if (!mohclass && !ast_strlen_zero(interpclass))
+	}
+	if (!mohclass && !ast_strlen_zero(interpclass)) {
 		mohclass = get_mohbyname(interpclass, 1);
-	AST_RWLIST_UNLOCK(&mohclasses);
+	}
 
 	/* If no moh class found in memory, then check RT */
 	if (!mohclass && ast_check_realtime("musiconhold")) {
+		struct ast_variable *var = NULL, *tmp = NULL;
+
 		if (!ast_strlen_zero(chan->musicclass)) {
 			var = ast_load_realtime("musiconhold", "name", chan->musicclass, NULL);
 		}
@@ -1203,18 +1210,18 @@
 					strcpy(mohclass->dir, "nodir");
 				} else {
 					ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", mohclass->name);
-					ast_free(mohclass);
+					mohclass = mohclass_unref(mohclass);
 					return -1;
 				}
 			}
 			if (ast_strlen_zero(mohclass->mode)) {
 				ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", mohclass->name);
-				ast_free(mohclass);
+				mohclass = mohclass_unref(mohclass);
 				return -1;
 			}
 			if (ast_strlen_zero(mohclass->args) && !strcasecmp(mohclass->mode, "custom")) {
 				ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", mohclass->name);
-				ast_free(mohclass);
+				mohclass = mohclass_unref(mohclass);
 				return -1;
 			}
 
@@ -1225,13 +1232,12 @@
 					ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
 					if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
 						/* we found RT class with the same name, seems like we should continue playing existing one */
-						ast_moh_free_class(&mohclass);
+						mohclass = mohclass_unref(mohclass);
 						mohclass = state->class;
 					}
 				}
 				moh_register(mohclass, 0);
 			} else {
-
 				/* We don't register RT moh class, so let's init it manualy */
 
 				time(&mohclass->start);
@@ -1239,7 +1245,7 @@
 	
 				if (!strcasecmp(mohclass->mode, "files")) {
 					if (!moh_scan_files(mohclass)) {
-						ast_moh_free_class(&mohclass);
+						mohclass = mohclass_unref(mohclass);
 						return -1;
 					}
 					if (strchr(mohclass->args, 'r'))
@@ -1263,7 +1269,7 @@
 					if (mohclass->pseudofd < 0) {
 						ast_log(LOG_WARNING, "Unable to open pseudo channel for timing...  Sound may be choppy.\n");
 					} else {
-						x = 320;
+						int x = 320;
 						ioctl(mohclass->pseudofd, DAHDI_SET_BLOCKSIZE, &x);
 					}
 #else
@@ -1275,54 +1281,50 @@
 						ast_log(LOG_NOTICE, "This channel already has a MOH class attached (%s)!\n", state->class->name);
 						if (state->class->realtime && !ast_test_flag(global_flags, MOH_CACHERTCLASSES) && !strcasecmp(mohclass->name, state->class->name)) {
 							/* we found RT class with the same name, seems like we should continue playing existing one */
-							ast_moh_free_class(&mohclass);
+							mohclass = mohclass_unref(mohclass);
 							mohclass = state->class;
-	
 						}
 					} else {
 						if (ast_pthread_create_background(&mohclass->thread, NULL, monmp3thread, mohclass)) {
 							ast_log(LOG_WARNING, "Unable to create moh...\n");
-							if (mohclass->pseudofd > -1)
+							if (mohclass->pseudofd > -1) {
 								close(mohclass->pseudofd);
-							ast_moh_free_class(&mohclass);
+								mohclass->pseudofd = -1;
+							}
+							mohclass = mohclass_unref(mohclass);
 							return -1;
 						}
 					}
 				} else {
 					ast_log(LOG_WARNING, "Don't know how to do a mode '%s' music on hold\n", mohclass->mode);
-					ast_moh_free_class(&mohclass);
+					mohclass = mohclass_unref(mohclass);
 					return -1;
 				}
-
 			}
-
-		} else if (var)
+		} else if (var) {
 			ast_variables_destroy(var);
-	}
-
-	
-
-	/* Requested MOH class not found, check for 'default' class in musiconhold.conf  */
+		}
+	}
+
 	if (!mohclass) {
-		AST_RWLIST_RDLOCK(&mohclasses);
 		mohclass = get_mohbyname("default", 1);
-		if (mohclass)
-			ast_atomic_fetchadd_int(&mohclass->inuse, +1);
-		AST_RWLIST_UNLOCK(&mohclasses);
+	}
+
+	if (!mohclass) {
+		return -1;
+	}
+
+	ast_set_flag(chan, AST_FLAG_MOH);
+
+	if (mohclass->total_files) {
+		res = ast_activate_generator(chan, &moh_file_stream, mohclass);
 	} else {
-		AST_RWLIST_RDLOCK(&mohclasses);
-		ast_atomic_fetchadd_int(&mohclass->inuse, +1);
-		AST_RWLIST_UNLOCK(&mohclasses);
-	}
-
-	if (!mohclass)
-		return -1;
-
-	ast_set_flag(chan, AST_FLAG_MOH);
-	if (mohclass->total_files) {
-		return ast_activate_generator(chan, &moh_file_stream, mohclass);
-	} else
-		return ast_activate_generator(chan, &mohgen, mohclass);
+		res = ast_activate_generator(chan, &mohgen, mohclass);
+	}
+
+	mohclass = mohclass_unref(mohclass);
+
+	return res;
 }
 
 static void local_ast_moh_stop(struct ast_channel *chan)
@@ -1337,6 +1339,77 @@
 			chan->stream = NULL;
 		}
 	}
+}
+
+static void moh_class_destructor(void *obj)
+{
+	struct mohclass *class = obj;
+	struct mohdata *member;
+
+	ast_debug(1, "Destroying MOH class '%s'\n", class->name);
+
+	if (class->pid > 1) {
+		char buff[8192];
+		int bytes, tbytes = 0, stime = 0, pid = 0;
+
+		ast_log(LOG_DEBUG, "killing %d!\n", class->pid);
+
+		stime = time(NULL) + 2;
+		pid = class->pid;
+		class->pid = 0;
+
+		/* Back when this was just mpg123, SIGKILL was fine.  Now we need
+		 * to give the process a reason and time enough to kill off its
+		 * children. */
+		killpg(pid, SIGHUP);
+		usleep(100000);
+		killpg(pid, SIGTERM);
+		usleep(100000);
+		killpg(pid, SIGKILL);
+
+		while ((ast_wait_for_input(class->srcfd, 100) > 0) && 
+				(bytes = read(class->srcfd, buff, 8192)) && time(NULL) < stime) {
+			tbytes = tbytes + bytes;
+		}
+
+		ast_log(LOG_DEBUG, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
+
+		close(class->srcfd);
+	}
+
+	while ((member = AST_LIST_REMOVE_HEAD(&class->members, list))) {
+		free(member);
+	}
+	
+	if (class->thread) {
+		pthread_cancel(class->thread);
+		class->thread = AST_PTHREADT_NULL;
+	}
+
+	if (class->filearray) {
+		int i;
+		for (i = 0; i < class->total_files; i++) {
+			free(class->filearray[i]);
+		}
+		free(class->filearray);
+		class->filearray = NULL;
+	}
+}
+
+static int moh_class_mark(void *obj, void *arg, int flags)
+{
+	struct mohclass *class = obj;
+
+	class->delete = 1;
+
+	return 0;
+}
+
+static int moh_classes_delete_marked(void *obj, void *arg, int flags)
+{
+	struct mohclass *class = obj;
+
+	return class->delete ? CMP_MATCH : 0;
 }
 
 static int load_moh_classes(int reload)
@@ -1354,11 +1427,7 @@
 		return 0;
 
 	if (reload) {
-		AST_RWLIST_WRLOCK(&mohclasses);
-		AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
-			class->delete = 1;
-		}
-		AST_RWLIST_UNLOCK(&mohclasses);
+		ao2_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL);
 	}
 	
 	ast_clear_flag(global_flags, AST_FLAGS_ALL);
@@ -1367,121 +1436,87 @@
 	for (; cat; cat = ast_category_browse(cfg, cat)) {
 		/* Setup common options from [general] section */
 		if (!strcasecmp(cat, "general")) {
-			var = ast_variable_browse(cfg, cat);
-			while (var) {
+			for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
 				if (!strcasecmp(var->name, "cachertclasses")) {
 					ast_set2_flag(global_flags, ast_true(var->value), MOH_CACHERTCLASSES);
 				} else {
 					ast_log(LOG_WARNING, "Unknown option '%s' in [general] section of musiconhold.conf\n", var->name);
 				}
-				var = var->next;
 			}
 		}
 		/* These names were deprecated in 1.4 and should not be used until after the next major release. */
-		if (strcasecmp(cat, "classes") && strcasecmp(cat, "moh_files") && strcasecmp(cat, "general")) {
-			if (!(class = moh_class_malloc()))
-				break;
-
-			ast_copy_string(class->name, cat, sizeof(class->name));	
-			var = ast_variable_browse(cfg, cat);
-			while (var) {
-				if (!strcasecmp(var->name, "mode"))
-					ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
-				else if (!strcasecmp(var->name, "directory"))
-					ast_copy_string(class->dir, var->value, sizeof(class->dir));
-				else if (!strcasecmp(var->name, "application"))
-					ast_copy_string(class->args, var->value, sizeof(class->args));
-				else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
-					class->digit = *var->value;
-				else if (!strcasecmp(var->name, "random"))
-					ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
-				else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
-					ast_set_flag(class, MOH_RANDOMIZE);
-				else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
-					ast_set_flag(class, MOH_SORTALPHA);
-				else if (!strcasecmp(var->name, "format")) {
-					class->format = ast_getformatbyname(var->value);
-					if (!class->format) {
-						ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
-						class->format = AST_FORMAT_SLINEAR;
-					}
-				}
-				var = var->next;
-			}
-
-			if (ast_strlen_zero(class->dir)) {
-				if (!strcasecmp(class->mode, "custom")) {
-					strcpy(class->dir, "nodir");
-				} else {
-					ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
-					ast_free(class);
-					continue;
+		if (!strcasecmp(cat, "classes") || !strcasecmp(cat, "moh_files") || 
+				!strcasecmp(cat, "general")) {
+			continue;
+		}
+
+		if (!(class = moh_class_malloc())) {
+			break;
+		}
+
+		ast_copy_string(class->name, cat, sizeof(class->name));	
+		for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
+			if (!strcasecmp(var->name, "mode"))
+				ast_copy_string(class->mode, var->value, sizeof(class->mode)); 
+			else if (!strcasecmp(var->name, "directory"))
+				ast_copy_string(class->dir, var->value, sizeof(class->dir));
+			else if (!strcasecmp(var->name, "application"))
+				ast_copy_string(class->args, var->value, sizeof(class->args));
+			else if (!strcasecmp(var->name, "digit") && (isdigit(*var->value) || strchr("*#", *var->value)))
+				class->digit = *var->value;
+			else if (!strcasecmp(var->name, "random"))
+				ast_set2_flag(class, ast_true(var->value), MOH_RANDOMIZE);
+			else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "random"))
+				ast_set_flag(class, MOH_RANDOMIZE);
+			else if (!strcasecmp(var->name, "sort") && !strcasecmp(var->value, "alpha")) 
+				ast_set_flag(class, MOH_SORTALPHA);
+			else if (!strcasecmp(var->name, "format")) {
+				class->format = ast_getformatbyname(var->value);
+				if (!class->format) {
+					ast_log(LOG_WARNING, "Unknown format '%s' -- defaulting to SLIN\n", var->value);
+					class->format = AST_FORMAT_SLINEAR;
 				}
 			}
-			if (ast_strlen_zero(class->mode)) {
-				ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
-				ast_free(class);
+		}
+
+		if (ast_strlen_zero(class->dir)) {
+			if (!strcasecmp(class->mode, "custom")) {
+				strcpy(class->dir, "nodir");
+			} else {
+				ast_log(LOG_WARNING, "A directory must be specified for class '%s'!\n", class->name);
+				class = mohclass_unref(class);
 				continue;
 			}
-			if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
-				ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
-				ast_free(class);
-				continue;
-			}
-
-			/* Don't leak a class when it's already registered */
-			moh_register(class, reload);
-
-			numclasses++;
-		}
+		}
+		if (ast_strlen_zero(class->mode)) {
+			ast_log(LOG_WARNING, "A mode must be specified for class '%s'!\n", class->name);
+			class = mohclass_unref(class);
+			continue;
+		}
+		if (ast_strlen_zero(class->args) && !strcasecmp(class->mode, "custom")) {
+			ast_log(LOG_WARNING, "An application must be specified for class '%s'!\n", class->name);
+			class = mohclass_unref(class);
+			continue;
+		}
+
+		/* Don't leak a class when it's already registered */
+		moh_register(class, reload);
+
+		numclasses++;
 	}
 
 	ast_config_destroy(cfg);
 
+	ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, 
+			moh_classes_delete_marked, NULL);
+
 	return numclasses;
 }
 
-static int ast_moh_destroy_one(struct mohclass *moh)
-{
-	char buff[8192];
-	int bytes, tbytes = 0, stime = 0, pid = 0;
-
-	if (moh) {
-		if (moh->pid > 1) {
-			ast_debug(1, "killing %d!\n", moh->pid);
-			stime = time(NULL) + 2;
-			pid = moh->pid;
-			moh->pid = 0;
-			/* Back when this was just mpg123, SIGKILL was fine.  Now we need
-			 * to give the process a reason and time enough to kill off its
-			 * children. */
-			killpg(pid, SIGHUP);
-			usleep(100000);
-			killpg(pid, SIGTERM);
-			usleep(100000);
-			killpg(pid, SIGKILL);
-			while ((ast_wait_for_input(moh->srcfd, 100) > 0) && (bytes = read(moh->srcfd, buff, 8192)) && time(NULL) < stime)
-				tbytes = tbytes + bytes;
-			ast_debug(1, "mpg123 pid %d and child died after %d bytes read\n", pid, tbytes);
-			close(moh->srcfd);
-		}
-		ast_moh_free_class(&moh);
-	}
-
-	return 0;
-}
-
 static void ast_moh_destroy(void)
 {
-	struct mohclass *moh;
-
 	ast_verb(2, "Destroying musiconhold processes\n");
-
-	AST_RWLIST_WRLOCK(&mohclasses);
-	while ((moh = AST_RWLIST_REMOVE_HEAD(&mohclasses, list))) {
-		ast_moh_destroy_one(moh);
-	}
-	AST_RWLIST_UNLOCK(&mohclasses);
+	ao2_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
 }
 
 static char *handle_cli_moh_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
@@ -1508,8 +1543,8 @@
 
 static char *handle_cli_moh_show_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	int i;
 	struct mohclass *class;
+	struct ao2_iterator i;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -1526,16 +1561,20 @@
 	if (a->argc != e->args)
 		return CLI_SHOWUSAGE;
 
-	AST_RWLIST_RDLOCK(&mohclasses);
-	AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
-		if (!class->total_files)
+	i = ao2_iterator_init(mohclasses, 0);
+
+	for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
+		int x;
+
+		if (!class->total_files) {
 			continue;
+		}
 
 		ast_cli(a->fd, "Class: %s\n", class->name);
-		for (i = 0; i < class->total_files; i++)
-			ast_cli(a->fd, "\tFile: %s\n", class->filearray[i]);
-	}
-	AST_RWLIST_UNLOCK(&mohclasses);
+		for (x = 0; x < class->total_files; x++) {
+			ast_cli(a->fd, "\tFile: %s\n", class->filearray[x]);
+		}
+	}
 
 	return CLI_SUCCESS;
 }
@@ -1543,6 +1582,7 @@
 static char *handle_cli_moh_show_classes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct mohclass *class;
+	struct ao2_iterator i;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -1558,20 +1598,19 @@
 	if (a->argc != e->args)
 		return CLI_SHOWUSAGE;
 
-	AST_RWLIST_RDLOCK(&mohclasses);
-	AST_RWLIST_TRAVERSE(&mohclasses, class, list) {
+	i = ao2_iterator_init(mohclasses, 0);
+
+	for (; (class = ao2_iterator_next(&i)); mohclass_unref(class)) {
 		ast_cli(a->fd, "Class: %s\n", class->name);
 		ast_cli(a->fd, "\tMode: %s\n", S_OR(class->mode, "<none>"));
 		ast_cli(a->fd, "\tDirectory: %s\n", S_OR(class->dir, "<none>"));
-		ast_cli(a->fd, "\tUse Count: %d\n", class->inuse);
-		if (class->digit)
-			ast_cli(a->fd, "\tDigit: %c\n", class->digit);
-		if (ast_test_flag(class, MOH_CUSTOM))
+		if (ast_test_flag(class, MOH_CUSTOM)) {
 			ast_cli(a->fd, "\tApplication: %s\n", S_OR(class->args, "<none>"));
-		if (strcasecmp(class->mode, "files"))
+		}
+		if (strcasecmp(class->mode, "files")) {
 			ast_cli(a->fd, "\tFormat: %s\n", ast_getformatname(class->format));
-	}
-	AST_RWLIST_UNLOCK(&mohclasses);
+		}
+	}
 
 	return CLI_SUCCESS;
 }
@@ -1582,30 +1621,35 @@
 	AST_CLI_DEFINE(handle_cli_moh_show_files,   "List MusicOnHold file-based classes")
 };
 
-static int init_classes(int reload) 
-{
-	struct mohclass *moh;
-    
-	if (!load_moh_classes(reload)) 		/* Load classes from config */
-		return 0;			/* Return if nothing is found */
-
-	AST_RWLIST_WRLOCK(&mohclasses);
-	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mohclasses, moh, list) {
-		if (reload && moh->delete) {
-			AST_RWLIST_REMOVE_CURRENT(list);
-			if (!moh->inuse)
-				ast_moh_destroy_one(moh);
-		}
-	}
-	AST_RWLIST_TRAVERSE_SAFE_END
-	AST_RWLIST_UNLOCK(&mohclasses);
-
-	return 1;
+static int moh_class_hash(const void *obj, const int flags)
+{
+	const struct mohclass *class = obj;
+
+	return ast_str_case_hash(class->name);
+}
+
+static int moh_class_cmp(void *obj, void *arg, int flags)
+{
+	struct mohclass *class = obj, *class2 = arg;
+
+	return strcasecmp(class->name, class2->name) ? 0 : CMP_MATCH | CMP_STOP;
 }
 
 static int load_module(void)
 {
 	int res;
+
+	if (!(mohclasses = ao2_container_alloc(53, moh_class_hash, moh_class_cmp))) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	if (!load_moh_classes(0)) { 	/* No music classes configured, so skip it */
+		ast_log(LOG_WARNING, "No music on hold classes configured, "
+				"disabling music on hold.\n");
+	} else {
+		ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
+				local_ast_moh_cleanup);
+	}
 
 	res = ast_register_application(play_moh, play_moh_exec, play_moh_syn, play_moh_desc);
 	ast_register_atexit(ast_moh_destroy);
@@ -1619,21 +1663,24 @@
 	if (!res)
 		res = ast_register_application(stop_moh, stop_moh_exec, stop_moh_syn, stop_moh_desc);
 
-	if (!init_classes(0)) { 	/* No music classes configured, so skip it */
-		ast_log(LOG_WARNING, "No music on hold classes configured, disabling music on hold.\n");
-	} else {
-		ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
-	}
-
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
 static int reload(void)
 {
-	if (init_classes(1))
-		ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop, local_ast_moh_cleanup);
-
-	return 0;
+	if (load_moh_classes(1)) {
+		ast_install_music_functions(local_ast_moh_start, local_ast_moh_stop,
+				local_ast_moh_cleanup);
+	}
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int moh_class_inuse(void *obj, void *arg, int flags)
+{
+	struct mohclass *class = obj;
+
+	return AST_LIST_EMPTY(&class->members) ? 0 : CMP_MATCH | CMP_STOP;
 }
 
 static int unload_module(void)
@@ -1641,20 +1688,20 @@
 	int res = 0;
 	struct mohclass *class = NULL;
 
-	AST_RWLIST_WRLOCK(&mohclasses);
-	AST_LIST_TRAVERSE(&mohclasses, class, list) {
-		if (class->inuse > 0) {
-			res = -1;
-			break;
-		}
-	}
-	AST_RWLIST_UNLOCK(&mohclasses);
+	/* XXX This check shouldn't be required if module ref counting was being used
+	 * properly ... */
+	if ((class = ao2_callback(mohclasses, 0, moh_class_inuse, NULL))) {
+		class = mohclass_unref(class);
+		res = -1;
+	}
+
 	if (res < 0) {
 		ast_log(LOG_WARNING, "Unable to unload res_musiconhold due to active MOH channels\n");
 		return res;
 	}
 
 	ast_uninstall_music_functions();
+
 	ast_moh_destroy();
 	res = ast_unregister_application(play_moh);
 	res |= ast_unregister_application(wait_moh);
@@ -1662,11 +1709,12 @@
 	res |= ast_unregister_application(start_moh);
 	res |= ast_unregister_application(stop_moh);
 	ast_cli_unregister_multiple(cli_moh, sizeof(cli_moh) / sizeof(struct ast_cli_entry));
+
 	return res;
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Music On Hold Resource",
-		.load = load_module,
-		.unload = unload_module,
-		.reload = reload,
-	       );
+	.load = load_module,
+	.unload = unload_module,
+	.reload = reload,
+);




More information about the asterisk-commits mailing list