implement flushEvents() flushDigits() setAutoHangup() and setHangupHook(). reworked dtmfhandler and some aspects relating to threadstate. folded in memory pool thing from mishehu. added more asserts to switch_core_file (coordinated w/ anthony on this)

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@5442 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Traun Leyden
2007-06-22 19:14:53 +00:00
parent 34d6c49870
commit 6f78befac9
9 changed files with 1220 additions and 407 deletions
+24 -5
View File
@@ -80,16 +80,16 @@ class input_callback_state_t(_object):
input_callback_state_t_swigregister = _freeswitch.input_callback_state_t_swigregister
input_callback_state_t_swigregister(input_callback_state_t)
S_HUP = _freeswitch.S_HUP
S_FREE = _freeswitch.S_FREE
S_RDLOCK = _freeswitch.S_RDLOCK
class CoreSession(_object):
__swig_setmethods__ = {}
__setattr__ = lambda self, name, value: _swig_setattr(self, CoreSession, name, value)
__swig_getmethods__ = {}
__getattr__ = lambda self, name: _swig_getattr(self, CoreSession, name)
def __init__(self): raise AttributeError, "No constructor defined"
__repr__ = _swig_repr
def __init__(self, *args):
this = _freeswitch.new_CoreSession(*args)
try: self.this.append(this)
except: self.this = this
__swig_destroy__ = _freeswitch.delete_CoreSession
__del__ = lambda self : None;
__swig_setmethods__["session"] = _freeswitch.CoreSession_session_set
@@ -98,9 +98,15 @@ class CoreSession(_object):
__swig_setmethods__["channel"] = _freeswitch.CoreSession_channel_set
__swig_getmethods__["channel"] = _freeswitch.CoreSession_channel_get
if _newclass:channel = _swig_property(_freeswitch.CoreSession_channel_get, _freeswitch.CoreSession_channel_set)
__swig_setmethods__["flags"] = _freeswitch.CoreSession_flags_set
__swig_getmethods__["flags"] = _freeswitch.CoreSession_flags_get
if _newclass:flags = _swig_property(_freeswitch.CoreSession_flags_get, _freeswitch.CoreSession_flags_set)
__swig_setmethods__["cb_state"] = _freeswitch.CoreSession_cb_state_set
__swig_getmethods__["cb_state"] = _freeswitch.CoreSession_cb_state_get
if _newclass:cb_state = _swig_property(_freeswitch.CoreSession_cb_state_get, _freeswitch.CoreSession_cb_state_set)
__swig_setmethods__["hook_state"] = _freeswitch.CoreSession_hook_state_set
__swig_getmethods__["hook_state"] = _freeswitch.CoreSession_hook_state_get
if _newclass:hook_state = _swig_property(_freeswitch.CoreSession_hook_state_get, _freeswitch.CoreSession_hook_state_set)
def answer(*args): return _freeswitch.CoreSession_answer(*args)
def preAnswer(*args): return _freeswitch.CoreSession_preAnswer(*args)
def hangup(*args): return _freeswitch.CoreSession_hangup(*args)
@@ -117,17 +123,26 @@ class CoreSession(_object):
def transfer(*args): return _freeswitch.CoreSession_transfer(*args)
def playAndGetDigits(*args): return _freeswitch.CoreSession_playAndGetDigits(*args)
def streamfile(*args): return _freeswitch.CoreSession_streamfile(*args)
def flushEvents(*args): return _freeswitch.CoreSession_flushEvents(*args)
def flushDigits(*args): return _freeswitch.CoreSession_flushDigits(*args)
def setAutoHangup(*args): return _freeswitch.CoreSession_setAutoHangup(*args)
def setHangupHook(*args): return _freeswitch.CoreSession_setHangupHook(*args)
def ready(*args): return _freeswitch.CoreSession_ready(*args)
def execute(*args): return _freeswitch.CoreSession_execute(*args)
def begin_allow_threads(*args): return _freeswitch.CoreSession_begin_allow_threads(*args)
def end_allow_threads(*args): return _freeswitch.CoreSession_end_allow_threads(*args)
def get_uuid(*args): return _freeswitch.CoreSession_get_uuid(*args)
def get_cb_args(*args): return _freeswitch.CoreSession_get_cb_args(*args)
def check_hangup_hook(*args): return _freeswitch.CoreSession_check_hangup_hook(*args)
def run_dtmf_callback(*args): return _freeswitch.CoreSession_run_dtmf_callback(*args)
CoreSession_swigregister = _freeswitch.CoreSession_swigregister
CoreSession_swigregister(CoreSession)
bridge = _freeswitch.bridge
PythonDTMFCallback = _freeswitch.PythonDTMFCallback
hanguphook = _freeswitch.hanguphook
dtmf_callback = _freeswitch.dtmf_callback
S_SWAPPED_IN = _freeswitch.S_SWAPPED_IN
S_SWAPPED_OUT = _freeswitch.S_SWAPPED_OUT
class PySession(CoreSession):
__swig_setmethods__ = {}
for _s in [CoreSession]: __swig_setmethods__.update(getattr(_s,'__swig_setmethods__',{}))
@@ -143,8 +158,12 @@ class PySession(CoreSession):
__swig_destroy__ = _freeswitch.delete_PySession
__del__ = lambda self : None;
def setDTMFCallback(*args): return _freeswitch.PySession_setDTMFCallback(*args)
def setHangupHook(*args): return _freeswitch.PySession_setHangupHook(*args)
def check_hangup_hook(*args): return _freeswitch.PySession_check_hangup_hook(*args)
def hangup(*args): return _freeswitch.PySession_hangup(*args)
def begin_allow_threads(*args): return _freeswitch.PySession_begin_allow_threads(*args)
def end_allow_threads(*args): return _freeswitch.PySession_end_allow_threads(*args)
def run_dtmf_callback(*args): return _freeswitch.PySession_run_dtmf_callback(*args)
PySession_swigregister = _freeswitch.PySession_swigregister
PySession_swigregister(PySession)
@@ -1,7 +1,7 @@
#include "freeswitch_python.h"
#define sanity_check(x) do { if (!session) { switch_log_printf(SWITCH_CHANNEL_LOG,SWITCH_LOG_ERROR, "session is not initalized\n"); return x;}} while(0)
#define init_vars() do { caller_profile.source = "mod_python"; } while(0)
#define init_vars() do { caller_profile.source = "mod_python"; swapstate = S_SWAPPED_IN; } while(0)
PySession::PySession() : CoreSession()
{
@@ -24,80 +24,107 @@ void PySession::setDTMFCallback(PyObject *pyfunc, char *funcargs)
sanity_check();
if (!PyCallable_Check(pyfunc)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "DTMF function is not a python function.");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "DTMF function is not a python function.\n");
return;
}
else {
cb_state.funcargs = funcargs;
cb_state.function = (void *) pyfunc;
Py_XINCREF(pyfunc);
CoreSession::setDTMFCallback((void *) pyfunc, funcargs);
args.buf = &cb_state;
args.buflen = sizeof(cb_state); // not sure what this is used for, copy mod_spidermonkey
// we cannot set the actual callback to a python function, because
// the callback is a function pointer with a specific signature.
// so, set it to the following c function which will act as a proxy,
// finding the python callback in the args callback args structure
args.input_callback = PythonDTMFCallback; // defined in mod_python.i
ap = &args;
}
void PySession::setHangupHook(PyObject *pyfunc) {
if (!PyCallable_Check(pyfunc)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Hangup hook is not a python function.\n");
return;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "dtmf callback was set, pyfunc: %p. cb_state: %p\n", pyfunc, &cb_state);
// without this Py_XINCREF, there will be segfaults. basically the python
// interpreter will not know that it should not GC this object.
// callback example: http://docs.python.org/ext/callingPython.html
Py_XINCREF(pyfunc);
CoreSession::setHangupHook((void *) pyfunc);
}
void PySession::begin_allow_threads(void) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PySession::begin_allow_threads() called\n");
// swap out threadstate and store in instance variable
threadState = (void *) PyEval_SaveThread();
cb_state.threadState = threadState;
args.buf = &cb_state;
ap = &args;
}
void PySession::end_allow_threads(void) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PySession::end_allow_threads() called\n");
// swap in threadstate from instance variable saved earlier
PyEval_RestoreThread(((PyThreadState *)threadState));
}
PySession::~PySession() {
// Should we do any cleanup here?
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PySession::~PySession desctructor\n");
}
/* ----- functions not bound to PySession instance ------ */
switch_status_t PythonDTMFCallback(switch_core_session_t *session,
void *input,
switch_input_type_t itype,
void *buf,
unsigned int buflen)
{
PyObject *func, *arglist;
void PySession::check_hangup_hook() {
PyObject *func;
PyObject *result;
char *resultStr;
char *funcargs;
input_callback_state_t *cb_state;
switch_file_handle_t *fh = NULL;
PyThreadState *threadState = NULL;
bool did_swap_in = false;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "check_hangup_hook called\n");
if (!session) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No valid session\n");
return;
}
// The did_swap_in boolean was added to fix the following problem:
// Design flaw - we swap in threadstate based on the assumption that thread state
// is currently _swapped out_ when this hangup hook is called. However, nothing known to
// guarantee that, and if thread state is already swapped in when this is invoked,
// bad things will happen.
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "check hangup hook end_allow_threads\n");
did_swap_in = end_allow_threads();
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PythonDTMFCallback\n");
if (on_hangup == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "on_hangup is null\n");
return;
}
if (!buf) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "buf pointer is null");
func = (PyObject *) on_hangup;
// TODO: to match js implementation, should pass the _python_ PySession
// object instance wrapping this C++ PySession instance. but how do we do that?
// for now, pass the uuid since its better than nothing
PyObject* func_arg = Py_BuildValue("(s)", uuid);
result = PyEval_CallObject(func, func_arg);
Py_XDECREF(func_arg);
if (result) {
resultStr = (char *) PyString_AsString(result);
// currently just ignore the result
}
else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to call python hangup callback\n");
PyErr_Print();
PyErr_Clear();
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "check hangup hook begin_allow_threads\n");
if (did_swap_in) {
begin_allow_threads();
}
Py_XDECREF(result);
}
switch_status_t PySession::run_dtmf_callback(void *input,
switch_input_type_t itype) {
PyObject *func, *arglist;
PyObject *pyresult;
char *resultStr;
char *funcargs;
switch_file_handle_t *fh = NULL;
bool did_swap_in = false;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "run_dtmf_callback\n");
if (!cb_state.function) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "cb_state->function is null\n");
return SWITCH_STATUS_FALSE;
}
cb_state = (input_callback_state *) buf;
}
func = (PyObject *) cb_state->function;
func = (PyObject *) cb_state.function;
if (!func) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "cb_state->function is null\n");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "cb_state->function is null\n");
return SWITCH_STATUS_FALSE;
}
else {
@@ -108,7 +135,7 @@ switch_status_t PythonDTMFCallback(switch_core_session_t *session,
return SWITCH_STATUS_FALSE;
}
funcargs = (char *) cb_state->funcargs;
funcargs = (char *) cb_state.funcargs;
arglist = Py_BuildValue("(sis)", input, itype, funcargs);
if (!arglist) {
@@ -116,36 +143,127 @@ switch_status_t PythonDTMFCallback(switch_core_session_t *session,
return SWITCH_STATUS_FALSE;
}
threadState = (PyThreadState *) cb_state->threadState;
if (!threadState) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error, invalid threadstate\n");
return SWITCH_STATUS_FALSE;
}
else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "restoring threadstate: %p\n", threadState);
}
PyEval_RestoreThread(threadState); // nasty stuff happens when py interp has no thread state
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "restored threadstate, calling python function: %p\n", func);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "run_dtmf_callback end_allow_threads\n");
did_swap_in = end_allow_threads();
result = PyEval_CallObject(func, arglist);
pyresult = PyEval_CallObject(func, arglist);
threadState = PyEval_SaveThread();
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "called python function\n");
Py_DECREF(arglist); // Trash arglist
if (result && result != Py_None) {
resultStr = (char *) PyString_AsString(result);
Py_XDECREF(result);
return process_callback_result(resultStr, cb_state, session);
Py_XDECREF(arglist); // Trash arglist
if (pyresult && pyresult != Py_None) {
resultStr = (char *) PyString_AsString(pyresult);
switch_status_t cbresult = process_callback_result(resultStr, &cb_state, session);
return cbresult;
}
else {
return SWITCH_STATUS_FALSE;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error calling python callback\n");
PyErr_Print();
PyErr_Clear();
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "run_dtmf_callback begin_allow_threads\n");
if (did_swap_in) {
begin_allow_threads();
}
Py_XDECREF(pyresult);
return SWITCH_STATUS_SUCCESS;
}
bool PySession::begin_allow_threads(void) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PySession::begin_allow_threads() called\n");
// swap out threadstate and store in instance variable
switch_channel_t *channel = switch_core_session_get_channel(session);
PyThreadState *swapin_tstate = (PyThreadState *) switch_channel_get_private(channel, "SwapInThreadState");
// so lets assume the thread state was swapped in when the python script was started,
// therefore swapin_tstate will be NULL (because there is nothing to swap in, since its
// _already_ swapped in.)
if (swapin_tstate == NULL) {
// currently swapped in
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Threadstate swap-out!\n");
swapin_tstate = PyEval_SaveThread();
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "swapin_tstate: %p\n", swapin_tstate);
// give future swapper-inners something to actually swap in
switch_channel_set_private(channel, "SwapInThreadState", (void *) swapin_tstate);
cb_state.threadState = threadState; // TODO: get rid of this
args.buf = &cb_state;
ap = &args;
return true;
}
else {
// currently swapped out
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Threadstate already swapd-out! Skipping\n");
return false;
}
}
bool PySession::end_allow_threads(void) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PySession::end_allow_threads() called\n");
// swap in threadstate from instance variable saved earlier
switch_channel_t *channel = switch_core_session_get_channel(session);
PyThreadState *swapin_tstate = (PyThreadState *) switch_channel_get_private(channel, "SwapInThreadState");
if (swapin_tstate == NULL) {
// currently swapped in
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Threadstate double swap-in! Skipping\n");
return false;
}
else {
// currently swapped out
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Threadstate swap-in!\n");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "swapin_tstate: %p\n", swapin_tstate);
PyEval_RestoreThread(swapin_tstate);
// dont give any swapper-inners the opportunity to do a double swap
switch_channel_set_private(channel, "SwapInThreadState", NULL);
return true;
}
}
void PySession::hangup(char *cause) {
// since we INCREF'd this function pointer earlier (so the py gc didnt reclaim it)
// we have to DECREF it, or else the PySession dtor will never get called and
// a zombie channel will be left over using up resources
if (cb_state.function != NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "xdecref on cb_state_function\n");
PyObject * func = (PyObject *) cb_state.function;
Py_XDECREF(func);
}
else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "cb_state.function is null\n");
}
CoreSession::hangup(cause);
}
PySession::~PySession() {
// Should we do any cleanup here?
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PySession::~PySession started\n");
if (on_hangup) {
PyObject * func = (PyObject *) on_hangup;
Py_XDECREF(func);
}
if (cb_state.function != NULL) {
PyObject * func = (PyObject *) cb_state.function;
Py_XDECREF(func);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "PySession::~PySession finished\n");
}
@@ -154,3 +272,6 @@ switch_status_t PythonDTMFCallback(switch_core_session_t *session,
@@ -14,12 +14,10 @@ extern "C" {
switch_status_t PythonDTMFCallback(switch_core_session *session,
void *input,
switch_input_type_t itype,
void *buf,
unsigned int buflen);
typedef enum {
S_SWAPPED_IN = (1 << 0),
S_SWAPPED_OUT = (1 << 1)
} swap_state_t;
void console_log(char *level_str, char *msg);
void console_clean_log(char *msg);
@@ -29,14 +27,21 @@ void api_reply_delete(char *reply);
class PySession : public CoreSession {
private:
void *threadState;
int swapstate;
public:
PySession();
PySession(char *uuid);
PySession(switch_core_session_t *session);
~PySession();
void setDTMFCallback(PyObject *pyfunc, char *funcargs);
void begin_allow_threads();
void end_allow_threads();
void setHangupHook(PyObject *pyfunc);
void check_hangup_hook();
void hangup(char *cause);
bool begin_allow_threads();
bool end_allow_threads();
switch_status_t run_dtmf_callback(void *input,
switch_input_type_t itype);
};
+34 -4
View File
@@ -49,11 +49,12 @@ PyThreadState *mainThreadState = NULL;
void init_freeswitch(void);
static switch_api_interface_t python_run_interface;
SWITCH_MODULE_LOAD_FUNCTION(mod_python_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_python_shutdown);
SWITCH_MODULE_DEFINITION(mod_python, mod_python_load, mod_python_shutdown, NULL);
static void eval_some_python(char *uuid, char *args)
static void eval_some_python(char *uuid, char *args, switch_core_session_t *session)
{
PyThreadState *tstate = NULL;
char *dupargs = NULL;
@@ -92,6 +93,11 @@ static void eval_some_python(char *uuid, char *args)
// swap in thread state
PyEval_AcquireThread(tstate);
if (session) {
// record the fact that thread state is swapped in
switch_channel_t *channel = switch_core_session_get_channel(session);
switch_channel_set_private(channel, "SwapInThreadState", NULL);
}
init_freeswitch();
// import the module
@@ -138,7 +144,9 @@ static void eval_some_python(char *uuid, char *args)
}
// invoke the handler
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Call python script \n");
result = PyEval_CallObjectWithKeywords(function, arg, (PyObject *)NULL);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Finished calling python script \n");
// check the result and print out any errors
if (!result) {
@@ -160,7 +168,29 @@ static void eval_some_python(char *uuid, char *args)
Py_XDECREF(result);
// swap out thread state
PyEval_ReleaseThread(tstate);
if (session) {
// record the fact that thread state is swapped in
switch_channel_t *channel = switch_core_session_get_channel(session);
PyThreadState *swapin_tstate = (PyThreadState *) switch_channel_get_private(channel, "SwapInThreadState");
// so lets assume nothing in the python script swapped any thread state in
// or out .. thread state will currently be swapped in, and the SwapInThreadState
// will be null
if (swapin_tstate == NULL) {
// swap it out
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Threadstate mod_python.c swap-out! \n");
// PyEval_ReleaseThread(cur_tstate);
swapin_tstate = (void *) PyEval_SaveThread();
switch_channel_set_private(channel, "SwapInThreadState", (void *) swapin_tstate);
}
else {
// thread state is already swapped out, so, nothing for us to do
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "according to chan priv data, already swapped out \n");
}
}
else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Threadstate mod_python.c swap-out! \n");
PyEval_ReleaseThread(tstate);
}
switch_safe_free(dupargs);
@@ -169,7 +199,7 @@ static void eval_some_python(char *uuid, char *args)
static void python_function(switch_core_session_t *session, char *data)
{
eval_some_python(switch_core_session_get_uuid(session), (char *)data);
eval_some_python(switch_core_session_get_uuid(session), (char *)data, session);
}
@@ -183,7 +213,7 @@ static void *SWITCH_THREAD_FUNC py_thread_run(switch_thread_t *thread, void *obj
switch_memory_pool_t *pool;
struct switch_py_thread *pt = (struct switch_py_thread *) obj;
eval_some_python(NULL, strdup(pt->args));
eval_some_python(NULL, strdup(pt->args), NULL);
pool = pt->pool;
switch_core_destroy_memory_pool(&pool);
@@ -9,12 +9,16 @@
%cstring_bounded_mutable(char *dtmf_buf, 128);
%cstring_bounded_mutable(char *terminator, 8);
/** insert the following includes into generated code so it compiles */
%{
#include "switch_cpp.h"
#include "freeswitch_python.h"
%}
%ignore SwitchToMempool;
/**
* tell swig to grok everything defined in these header files and
* build all sorts of c wrappers and python shadows of the c wrappers.
File diff suppressed because it is too large Load Diff