mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-07-06 04:11:52 +00:00
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:
@@ -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,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user