# Asterisk Project : MacroExclusive()

Added by mdavenport , edited by mdavenport on Aug 27, 2010

# About the MacroExclusive application

By: Steve Davies <steve@connection-telecom.com

The MacroExclusive application was added to solve the problem of synchronization between calls running at the same time.

This is usually an issue when you have calls manipulating global variables or the Asterisk database, but may be useful elsewhere.

Consider this example macro, intended to return a "next" number - each caller is intended to get a different number:

    [macro-next]
    exten => s,1,Set(RESULT=${COUNT})
    exten => s,n,SetGlobalVar(COUNT=$[${COUNT} + 1])

The problem is that in a box with high activity, you can be sure that two calls will come along together - both will get the same "RESULT", or the "COUNT" value will get mangled.

Calling this Macro via MacroExclusive will use a mutex to make sure that only one call executes in the Macro at a time. This ensures that the two lines execute as a unit.

Note that even the s,2 line above has its own race problem. Two calls running that line at once will step on each other and the count will end up as +1 rather than +2.

I've also been able to use MacroExclusive where I have two Macros that need to be mutually exclusive.

Here's the example:

    [macro-push]
    ; push value ${ARG2} onto stack ${ARG1}
    exten => s,1,Set(DB(STACK/${ARG1})=${ARG2}^${DB(STACK/${ARG1})})

    [macro-pop]
    ; pop top value from stack ${ARG1}
    exten => s,1,Set(RESULT=${DB(STACK/${ARG1})})
    exten => s,n,Set(DB(STACK/${ARG1})=${CUT(RESULT,^,2)})
    exten => s,n,Set(RESULT=${CUT(RESULT,^,1)})

All that futzing with the STACK/${ARG1} in the astdb needs protecting if this is to work. But neither push nor pop can run together.

So add this "pattern":

    [macro-stack]
    exten => Macro(${ARG1},${ARG2},${ARG3})

... and use it like so:

    exten => s,1,MacroExclusive(stack,push,MYSTACK,bananas)
    exten => s,n,MacroExclusive(stack,push,MYSTACK,apples)
    exten => s,n,MacroExclusive(stack,push,MYSTACK,guavas)
    exten => s,n,MacroExclusive(stack,push,MYSTACK,pawpaws)
    exten => s,n,MacroExclusive(stack,pop,MYSTACK) ; RESULT gets pawpaws (yum)
    exten => s,n,MacroExclusive(stack,pop,MYSTACK) ; RESULT gets guavas
    exten => s,n,MacroExclusive(stack,pop,MYSTACK) ; RESULT gets apples
    exten => s,n,MacroExclusive(stack,pop,MYSTACK) ; RESULT gets bananas

We get to the push and pop macros "via" the stack macro. But only one call can execute the stack macro at a time; ergo, only one of push OR pop can run at a time.

Hope people find this useful.

Lastly, its worth pointing out that only Macros that access shared data will require this MacroExclusive protection. And Macro's that you call with macroExclusive should run quickly or you will clog up your Asterisk system.