aboutsummaryrefslogtreecommitdiffstats
path: root/doc/macroexclusive.txt
blob: 3a31114936b978492a9098038eb95137c30ef16f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
About the MacroExclusive application
------------------------------------

Steve Davies <steve@connection-telecom.com


The MacroExclusive application was added to solve the problem of
synchronisation 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.

Regards,
Steve