[asterisk-commits] russell: trunk r39681 - in /trunk: apps/app_macro.c include/asterisk/pbx.h pbx.c

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Sun Aug 13 20:24:07 MST 2006


Author: russell
Date: Sun Aug 13 22:24:06 2006
New Revision: 39681

URL: http://svn.digium.com/view/asterisk?rev=39681&view=rev
Log:
add MacroExclusive application, a Macro that only one call can executed at
a time (issue #7366, Steve Davies, with mods by me as discussed in the report)

Modified:
    trunk/apps/app_macro.c
    trunk/include/asterisk/pbx.h
    trunk/pbx.c

Modified: trunk/apps/app_macro.c
URL: http://svn.digium.com/view/asterisk/trunk/apps/app_macro.c?rev=39681&r1=39680&r2=39681&view=diff
==============================================================================
--- trunk/apps/app_macro.c (original)
+++ trunk/apps/app_macro.c Sun Aug 13 22:24:06 2006
@@ -70,6 +70,13 @@
 "(otherwise <macroname_b> if provided)\n"
 "Arguments and return values as in application macro()\n";
 
+static char *exclusive_descrip =
+"  MacroExclusive(macroname|arg1|arg2...):\n"
+"Executes macro defined in the context 'macro-macroname'\n"
+"Only one call at a time may run the macro.\n"
+"(we'll wait if another call is busy executing in the Macro)\n"
+"Arguments and return values as in application Macro()\n";
+
 static char *exit_descrip =
 "  MacroExit():\n"
 "Causes the currently running macro to exit as if it had\n"
@@ -79,15 +86,17 @@
 
 static char *app = "Macro";
 static char *if_app = "MacroIf";
+static char *exclusive_app = "MacroExclusive";
 static char *exit_app = "MacroExit";
 
 static char *synopsis = "Macro Implementation";
 static char *if_synopsis = "Conditional Macro Implementation";
+static char *exclusive_synopsis = "Exclusive Macro Implementation";
 static char *exit_synopsis = "Exit From Macro";
 
 LOCAL_USER_DECL;
 
-static int macro_exec(struct ast_channel *chan, void *data)
+static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
 {
 	const char *s;
 
@@ -140,14 +149,28 @@
 		LOCAL_USER_REMOVE(u);
 		return 0;
 	}
+
 	snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
 	if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
-  		if (!ast_context_find(fullmacro)) 
+		if (!ast_context_find(fullmacro)) 
 			ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
 		else
-	  		ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
+			ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
 		LOCAL_USER_REMOVE(u);
 		return 0;
+	}
+
+	/* If we are to run the macro exclusively, take the mutex */
+	if (exclusive) {
+		ast_log(LOG_DEBUG, "Locking macrolock for '%s'\n", fullmacro);
+		ast_autoservice_start(chan);
+		if (ast_context_lockmacro(fullmacro)) {
+			ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
+			ast_autoservice_stop(chan);
+			LOCAL_USER_REMOVE(u);
+			return 0;
+		}
+		ast_autoservice_stop(chan);
 	}
 	
 	/* Save old info */
@@ -243,7 +266,6 @@
 	snprintf(depthc, sizeof(depthc), "%d", depth);
 	if (!dead) {
 		pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
-
 		ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
 	}
 
@@ -302,8 +324,28 @@
 		pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
 	if (save_macro_offset)
 		free(save_macro_offset);
+
+	/* Unlock the macro */
+	if (exclusive) {
+		ast_log(LOG_DEBUG, "Unlocking macrolock for '%s'\n", fullmacro);
+		if (ast_context_unlockmacro(fullmacro)) {
+			ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
+			res = 0;
+		}
+	}
+	
 	LOCAL_USER_REMOVE(u);
 	return res;
+}
+
+static int macro_exec(struct ast_channel *chan, void *data)
+{
+	_macro_exec(chan, data, 0);
+}
+
+static int macroexclusive_exec(struct ast_channel *chan, void *data)
+{
+	_macro_exec(chan, data, 1);
 }
 
 static int macroif_exec(struct ast_channel *chan, void *data) 
@@ -350,6 +392,7 @@
 	res = ast_unregister_application(if_app);
 	res |= ast_unregister_application(exit_app);
 	res |= ast_unregister_application(app);
+	res |= ast_unregister_application(exclusive_app);
 
 	STANDARD_HANGUP_LOCALUSERS;
 
@@ -362,6 +405,7 @@
 
 	res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
 	res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
+	res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
 	res |= ast_register_application(app, macro_exec, synopsis, descrip);
 
 	return res;

Modified: trunk/include/asterisk/pbx.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/pbx.h?rev=39681&r1=39680&r2=39681&view=diff
==============================================================================
--- trunk/include/asterisk/pbx.h (original)
+++ trunk/include/asterisk/pbx.h Sun Aug 13 22:24:06 2006
@@ -681,6 +681,29 @@
  */
 int ast_unlock_context(struct ast_context *con);
 
+/*! 
+ * \brief locks the macrolock in the given given context
+ *
+ * \param macrocontext name of the macro-context to lock
+ *
+ * Locks the given macro-context to ensure only one thread (call) can execute it at a time
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_context_lockmacro(const char *macrocontext);
+
+/*!
+ * \brief Unlocks the macrolock in the given context
+ *
+ * \param macrocontext name of the macro-context to unlock
+ *
+ * Unlocks the given macro-context so that another thread (call) can execute it
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_context_unlockmacro(const char *macrocontext);
 
 int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority);
 

Modified: trunk/pbx.c
URL: http://svn.digium.com/view/asterisk/trunk/pbx.c?rev=39681&r1=39680&r2=39681&view=diff
==============================================================================
--- trunk/pbx.c (original)
+++ trunk/pbx.c Sun Aug 13 22:24:06 2006
@@ -165,6 +165,7 @@
 	struct ast_ignorepat *ignorepats;	/*!< Patterns for which to continue playing dialtone */
 	const char *registrar;			/*!< Registrar */
 	AST_LIST_HEAD_NOLOCK(, ast_sw) alts;	/*!< Alternative switches */
+	ast_mutex_t macrolock;			/*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
 	char name[0];				/*!< Name of the context */
 };
 
@@ -2742,6 +2743,62 @@
 }
 
 
+/*!
+ * \note This function locks contexts list by &conlist, searches for the right context
+ * structure, and locks the macrolock mutex in that context.
+ * macrolock is used to limit a macro to be executed by one call at a time.
+ */
+int ast_context_lockmacro(const char *context)
+{
+	struct ast_context *c = NULL;
+	int ret = -1;
+
+	ast_lock_contexts();
+
+	while ((c = ast_walk_contexts(c))) {
+		if (!strcmp(ast_get_context_name(c), context)) {
+			ret = 0;
+			break;
+		}
+	}
+
+	ast_unlock_contexts();
+
+	/* if we found context, lock macrolock */
+	if (ret == 0) 
+		ret = ast_mutex_lock(&c->macrolock);
+
+	return ret;
+}
+
+/*!
+ * \note This function locks contexts list by &conlist, searches for the right context
+ * structure, and unlocks the macrolock mutex in that context.
+ * macrolock is used to limit a macro to be executed by one call at a time.
+ */
+int ast_context_unlockmacro(const char *context)
+{
+	struct ast_context *c = NULL;
+	int ret = -1;
+
+	ast_lock_contexts();
+
+	while ((c = ast_walk_contexts(c))) {
+		if (!strcmp(ast_get_context_name(c), context)) {
+			ret = 0;
+			break;
+		}
+	}
+
+	ast_unlock_contexts();
+
+	/* if we found context, unlock macrolock */
+	if (ret == 0) 
+		ret = ast_mutex_unlock(&c->macrolock);
+
+	return ret;
+}
+
 /*! \brief Dynamically register a new dial plan application */
 int ast_register_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description)
 {
@@ -3451,6 +3508,7 @@
 	}
 	if ((tmp = ast_calloc(1, length))) {
 		ast_mutex_init(&tmp->lock);
+		ast_mutex_init(&tmp->macrolock);
 		strcpy(tmp->name, name);
 		tmp->root = NULL;
 		tmp->registrar = registrar;



More information about the asterisk-commits mailing list