[asterisk-users] Asterisk / PHP-AGI / pthreads
Satish Barot
satish4asterisk at gmail.com
Fri Jun 21 00:03:51 CDT 2013
On Thu, Jun 20, 2013 at 10:54 PM, Steve Edwards
<asterisk.org at sedwards.com>wrote:
> On Thu, 20 Jun 2013, Satish Barot wrote:
>
> Would you mind sharing a sample of your pthread-ed C AGI? This will help
>> someone like me who has written AGI in Perl/PHP and now exploring C AGI.
>>
>
> The source code for this particular AGI is about 600 lines and uses my own
> AGI library (written before other C AGI libraries were available) so I
> don't think it has a whole lot of value to other programmers.
>
> First a little background on why I used pthreads in this AGI...
>
> The application was an adult chat platform. (Mostly) guys call in to chat
> to (mostly) women and pay $1 to $4 per minute. At the end of the call, the
> accumulated charges are billed to their their credit card.
>
> Charges to credit cards usually take 2 transactions. The first transaction
> ('OPEN') places a temporary 'hold' on the card for $XXX. At the end of the
> call, a second transaction ('SALE') finalizes the billing for the actual
> charges for the call.
>
> (This '2 step' process is why you may have trouble charging an expensive
> dinner after checking into a hotel -- the hotel may have placed a 'hold'
> against your credit limit for the expected charge for your entire stay.)
>
> As originally implemented, Asterisk would play a prompt asking the caller
> to enter their card details. After the caller entered a 'valid' card number
> (that passes Luhn mod 10) and expiration date (between the current month &
> current year and current year + 7). My AGI would then play a prompt
> ('Please wait while your card is being verified') and then transmit the
> card details to our card processor. Getting a response from the card
> processor takes a second or so. My AGI returned the card processor response
> code as the 'priority' so the dialplan could continue to the main menu or
> play a prompt indicating why the card details failed. After 3 failures, a
> caller was played a prompt instructing them on alternative billing methods
> (high rate NPAs, check by phone, etc).
>
> This client was extremely particular about the 'caller experience' and
> thought the 'second or so' of silence while the card was being verified was
> excessive and had to be eliminated.
>
> Since the card processing time was not under my control, my solution was
> to change my AGI to create a second thread to play the 'please wait' prompt
> while the 'main line' code shipped off the card details to the processor.
>
> When I got the processor's response (which was usually before the prompt
> finished playing), the 'main line' code would 'join' the prompt thread. If
> the prompt thread was finished playing the prompt, execution continued
> immediately. If the prompt thread was still playing the prompt, execution
> continued as soon as the prompt thread received the AGI response from
> Asterisk. None of this took any special programming or synchronizing from
> me. It all 'just worked' because of the way pthreads are implemented.
>
> So, what did I learn that would be of value to you? Really understand the
> AGI protocol and you won't make a bunch of silly mistakes.
>
> The AGI protocol is very simple:
>
> 1) Read the AGI environment from stdin before you do anything in your AGI
> that interacts with Asterisk.
>
> 2) Write your AGI request to stdout.
>
> 3) Read Asterisk's AGI response from stdin.
>
> 4) Rinse, lather, repeat (steps 2 & 3).
>
> If you use an established AGI library (like you really, really should),
> this should be taken care of automagically, but understanding what's really
> going on will help when things don't work as expected.
>
> The number 1 mistake new AGI programmers make is not reading the AGI
> environment. Sometimes they 'get away' with it, some times they don't.
>
> The number 2 mistake is not reading the responses (step 3). Again,
> sometimes it works, sometimes it doesn't
>
> The number 3 mistake new (and old salts like me still make on occasion) is
> doing any I/O on stdin or stdout. A lot of bad debugging centers around
> 'throw in a printf somewhere to see what's going on' instead of using 'real
> programmer' tools like gdb*.
>
> If you're using an established AGI library, the crucial relationship
> between steps 2 and 3 will be take care of -- unless you're using multiple
> threads.
>
> If thread 1 issues an AGI request (writing to stdout) and then thread 2
> issues an AGI request (also writing to stdout) before thread 1 reads it's
> response, you've broken the protocol and bad things will happen. Maybe not
> immediately, but certainly when you're demonstrating the system to your
> boss.
>
> So my first suggestion (after using an established library and without
> knowing what your use case is) would be to 'designate' a single thread as
> 'the Asterisk' thread and only interact (via stdin and stdout) with
> Asterisk in that thread. If that's not feasible, you're going to need some
> sort of 'semaphore' mechanism so you don't 'intermingle' your AGI requests
> and responses and thereby violate the AGI protocol.
>
> Another couple of suggestions unrelated to threads, but are best (IMNSHO)
> practices for AGIs in general:
>
> 1) Use getopt_long(). Most (everybody but me?) seems to like vague,
> conflicting, and impossible to document or remember AGI command line
> options. Seriously, which would you rather see in "the other guy's"
> dialplan 5 years after he's gone:
>
> exten = *,n,agi(foo|5555551212|10255|**d)
>
> or
>
> exten = *,n,agi(foo,--debug,--ani=**5555551212,--charge=10255)
>
> 2) Use syslog() to say what's going on in your AGI. You can vary the level
> of 'verbosity' by signals or by command line options (see #1).
>
> Sheesh I'm long winded :)
>
> If you still think it would have value to you after reading this email,
> please let me know.
>
> *) A valuable debugging technique is to enable AGI debugging in the
> Asterisk CLI and capture the session when your AGI executes. This file can
> be munged into a file of the AGI environment and the AGI responses. Then,
> when you have your AGI loaded into gdb you can 'run' it specifying this
> file as input ('r <my-agi-input') and step through your AGI with some
> serious debugging horsepower behind you. Note that this technique is
> completely separate from Asterisk and can even take place on a separate
> computer that doesn't even have Asterisk installed.
>
>
> --
> Thanks in advance,
> ------------------------------**------------------------------**
> -------------
> Steve Edwards sedwards at sedwards.com Voice: +1-760-468-3867 PST
> Newline Fax: +1-760-731-3000
>
>
:) You reminded me of my teacher of old school days.
Very well explained.
I have somewhat similar requirement where I need to play some announcements
to entertain a caller while passing/processing some data through webservice
call ().
Thanks a lot for your response.
--Satish Barot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-users/attachments/20130621/fe67c1d2/attachment.htm>
More information about the asterisk-users
mailing list