Mod python dev
From FreeSWITCH Wiki
!!Totally out of date since rewrite in subversion 8998-9052
Contents |
Adding a new API method
Where to add the code?
freeswitch_python.h/cpp contains a PySession object that overrides the CoreSession object, which is defined in src/switch_cpp.h/cpp. Code should ideally be added to switch_cpp.h/cpp so that it is generic and re-usable by other language modules. If necessary, python specific code can be added to freeswitch_python.h/cpp and override the superclass code.
Code flow for DTMF callbacks
The following is true for the streamFile API function
- c code invokes python script (embedded python) via the mod_python.c code
- python code calls freeswitch_python.cpp via the swig generated python, then through the swig genrated cpp wrapper
- setting callback: in freeswitch_python.cpp, it sets the actual callback (args.input_callback) to a function defined in mod_python.i called PythonDTMFCallback. it then sets the cb_state.function pointer to thee PyObject * pyfunc that is passed in via the script -- this will be passed to PythonDTMFCallback and will be invoked. In fact, PythonDTFMCallback is acting a bit like a proxy here.
- PythonDTMFCallback sets the threadstate, then invokes the pyfunc python callback (embedded python)
Rebuilding
NOTE: You must use Swig version 1.3.31 (or later). Older versions are not known to work. Actually newer versions aren't known to work. Just use 1.3.31.
$ cd /path/to/mod_python $ make reswig $ make install
Things to Avoid
Dropping threadstate
Don't do this.
begin_allow_threads(); <-- swap out and save threadstate, good!
if (switch_ivr_originate(....) {
<--- woops, forgot to swap threadstate back in for this particular case!! hello segfault.
return SWITCH_STATUS_FALSE;
}
end_allow_threads(); <-- swap in saved threadstate, good
If you do, you will end up with a backtrace ending in ...
eip = 0xb421eea7 in PyEval_EvalFrameEx (Python/ceval.c:2597);
and it is interesting to look at the code that is called on that line in ceval.c (part of python interpeter)
--PyThreadState_GET()->recursion_depth
So when PyThreadState_GET() returns NULL, because of error above, the python interpreter segfaults and the process crashes.
FAQ
How does swig stuff work?
It builds a c++ wrapper around PySession (and therefore base class CoreSession), then builds a python wrapper around _that_ (so called, "shadow"), and thats what python scripts access.
Whats all this threadstate stuff?
In order to allow liveliness in other threads, threadstate must be swapped out and saved before a potentially long running call into the freeswitch core. The best example of this is the switch_ivr_play_file call, which can block for a long time. If threadstate remained swapped in for the thread that invoked switch_ivr_play_file, all other threads would remain blocked waiting for this to finish. So, threadstate must be swapped out.
Read up on Thread State and the Global Interpreter Lock. Another good source of information is the Apache mod_python source base.
CoreSession getDigits() returns int?
Certain functions like CoreSession.getDigits() returns an int value, yet the actual value returned to a python script is a tuple. How does that happen? The key to the magic is the following directive in mod_python.i
/** * tell swig to treat these variables as mutable so they * can be used to return values. * See http://www.swig.org/Doc1.3/Library.html */ %cstring_bounded_mutable(char *dtmf_buf, 128); %cstring_bounded_mutable(char *terminator, 8);
So when swig sees dtmf_buf in the parameter list, it says "Ah, that char * is a return value", and it so it returns a tuple instead of an int, with the value of dtmf_buf tacked on as the second tuple item.
How do hangup hooks work?
Its a callback you assign that is executeted as soone as the call is hungup or transferred, so you can use it to be notified the exact instant you should give up control of the scope.
Q: But a hangup hook gets called in the same thread that is in streamFile, or getDigits, or whatever, right? Not in a new one?
A: Right. On the inside it's the state change hook, which is attached to the channel when you start the script, and the instant something you are doing changes the state the hook calls the c callback. This gives you the chance to call your script callback.
Q: But that means the hangup hook can only be called when you're executing in a freeswitch application, when the script is running it won't get called asynchronously, yes or no?
A: The hangup hook is embedded into the the channel (rather, the state change hook), so no matter what tries to change the state, or when or how it is changed, it will trigger the callback.
The only case it would be called by a different thread is if you make an application that pulls up a channel by name from the hash and hangs it up (which is discouraged practice).
