Add hangup handler for Originate, expose state handlers in general. Various fixes.

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@14861 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Michael Giagnocavo 2009-09-15 00:16:13 +00:00
parent 88b15b9e09
commit 26df0f6881
9 changed files with 54911 additions and 55509 deletions

View File

@ -21,6 +21,7 @@ static SWIG_CSharpStringHelperCallback SWIG_csharp_string_callback_real = NULL;
%} %}
%pragma(csharp) imclasscode=%{ %pragma(csharp) imclasscode=%{
protected class SWIGStringHelper { protected class SWIGStringHelper {
public delegate string SWIGStringDelegate(string message); public delegate string SWIGStringDelegate(string message);
@ -120,13 +121,14 @@ char * SWIG_csharp_string_callback(const char * str) {
%ignore run_dtmf_callback; %ignore run_dtmf_callback;
%ignore setDTMFCallback; %ignore setDTMFCallback;
// These methods need a bit of wrapping help
%csmethodmodifiers CoreSession::originate "protected";
// Rename some things to make them more .NET-like // Rename some things to make them more .NET-like
//%csmethodmodifiers CoreSession::hangup "internal";
%rename (Answer) CoreSession::answer; %rename (Answer) CoreSession::answer;
%rename (Hangup) CoreSession::hangup; %rename (Hangup) CoreSession::hangup;
%rename (Ready) CoreSession::ready; %rename (Ready) CoreSession::ready;
%rename (Transfer) CoreSession::transfer; %rename (Transfer) CoreSession::transfer;
%rename (Originate) CoreSession::originate;
%rename (SetVariable) CoreSession::setVariable; %rename (SetVariable) CoreSession::setVariable;
%rename (GetVariable) CoreSession::getVariable; %rename (GetVariable) CoreSession::getVariable;
%rename (SetPrivate) CoreSession::setPrivate; %rename (SetPrivate) CoreSession::setPrivate;

View File

@ -69,7 +69,6 @@ ManagedSession::~ManagedSession()
// Do auto-hangup ourselves because CoreSession can't call check_hangup_hook // Do auto-hangup ourselves because CoreSession can't call check_hangup_hook
// after ManagedSession destruction (cause at point it's pure virtual) // after ManagedSession destruction (cause at point it's pure virtual)
if (session) { if (session) {
channel = switch_core_session_get_channel(session);
if (switch_test_flag(this, S_HUP) && !switch_channel_test_flag(channel, CF_TRANSFER)) { if (switch_test_flag(this, S_HUP) && !switch_channel_test_flag(channel, CF_TRANSFER)) {
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
setAutoHangup(0); setAutoHangup(0);
@ -104,3 +103,6 @@ switch_status_t ManagedSession::run_dtmf_callback(void *input, switch_input_type
return status; return status;
} }
static switch_status_t stateHangupHandler(switch_core_session_t *session) {
return SWITCH_STATUS_FALSE;
}

View File

@ -41,7 +41,6 @@ SWITCH_BEGIN_EXTERN_C
typedef void (*hangupFunction)(void); typedef void (*hangupFunction)(void);
typedef char* (*inputFunction)(void*, switch_input_type_t); typedef char* (*inputFunction)(void*, switch_input_type_t);
#ifndef _MANAGED #ifndef _MANAGED
#include <glib.h> #include <glib.h>
#include <mono/jit/jit.h> #include <mono/jit/jit.h>
@ -136,7 +135,6 @@ public:
virtual bool begin_allow_threads(); virtual bool begin_allow_threads();
virtual bool end_allow_threads(); virtual bool end_allow_threads();
virtual void check_hangup_hook(); virtual void check_hangup_hook();
virtual switch_status_t run_dtmf_callback(void *input, switch_input_type_t itype); virtual switch_status_t run_dtmf_callback(void *input, switch_input_type_t itype);
// P/Invoke function pointer to delegates // P/Invoke function pointer to delegates

File diff suppressed because it is too large Load Diff

View File

@ -103,7 +103,7 @@ namespace FreeSWITCH.Native {
/*** Times ***/ /*** Times ***/
const string created_time = "created_time"; const string created_time = "created_time";
const string answered_time = "answered_time"; const string answered_time = "answered_time";
const string hangup_time = "hangup_time"; const string hangup_time = "hungup_time";
const string progress_time = "progress_time"; const string progress_time = "progress_time";
const string transfer_time = "transfer_time"; const string transfer_time = "transfer_time";

View File

@ -295,6 +295,7 @@ namespace FreeSWITCH {
pi.Manager.BlockUntilUnloadIsSafe(); pi.Manager.BlockUntilUnloadIsSafe();
pi.Manager = null; pi.Manager = null;
pi.Domain = null; pi.Domain = null;
// This can crash if there is still sessions in the appdomain. Plugin code should use dispose properly.
AppDomain.Unload(d); AppDomain.Unload(d);
Log.WriteLine(LogLevel.Info, "Unloaded {0}, domain {1}.", pi.FileName, friendlyName); Log.WriteLine(LogLevel.Info, "Unloaded {0}, domain {1}.", pi.FileName, friendlyName);
} catch (Exception ex) { } catch (Exception ex) {

View File

@ -43,17 +43,21 @@ namespace FreeSWITCH.Native
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void CdeclAction(); delegate void CdeclAction();
// This callback is used for originate
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
public delegate switch_status_t switch_state_handler_t_delegate(IntPtr sessionPtr);
public partial class ManagedSession public partial class ManagedSession
{ {
// SWITCH_DECLARE(void) InitManagedSession(ManagedSession *session, MonoObject *dtmfDelegate, MonoObject *hangupDelegate) // SWITCH_DECLARE(void) InitManagedSession(ManagedSession *session, MonoObject *dtmfDelegate, MonoObject *hangupDelegate)
[DllImport("mod_managed.dll", CharSet = CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)] [DllImport("mod_managed.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern void InitManagedSession(IntPtr sessionPtr, DtmfCallback dtmfDelegate, CdeclAction hangupDelegate); static extern void InitManagedSession(IntPtr sessionPtr, DtmfCallback dtmfDelegate, CdeclAction hangupDelegate);
/// <summary>Initializes the native ManagedSession. Must be called after Originate.</summary> /// <summary>Initializes the native ManagedSession. Called after Originate completes successfully .</summary>
public void Initialize() internal void Initialize()
{ {
if (allocated == 0) { if (allocated == 0) {
Log.WriteLine(LogLevel.Critical, "Cannot initialize a ManagedSession until it is allocated (originated successfully)."); throw new InvalidOperationException("Cannot initialize a ManagedSession until it is allocated (originated successfully).");
} }
// P/Invoke generated function pointers stick around until the delegate is collected // P/Invoke generated function pointers stick around until the delegate is collected
// By sticking the delegates in fields, their lifetime won't be less than the session // By sticking the delegates in fields, their lifetime won't be less than the session
@ -121,6 +125,88 @@ namespace FreeSWITCH.Native
} }
} }
[Obsolete("Use static Originate method.", false)]
public bool Originate(CoreSession aLegSession, string destination, TimeSpan timeout) {
var res = 0 == this.originate(aLegSession, destination, (int)timeout.TotalMilliseconds, null);
if (res) {
this.Initialize();
}
return res;
}
// Creating these function pointers is a two-stage process.
// The delegate needs to be stored so it doesn't get GC'd, so we can't just return GetFunctionPointerForDelegate right away.
/// <summary>Wraps a nice handler into a delegate suitable for reverse P/Invoke. This only currently works well for hangup/reporting handlers.</summary>
public static switch_state_handler_t_delegate CreateStateHandlerDelegate(Action<ManagedSession> handler) {
// We create a ManagedSession on top of the session so callbacks can use it "nicely"
// Then we sort of dispose it.
switch_state_handler_t_delegate del = ptr => {
using (var sess = new ManagedSession(new SWIGTYPE_p_switch_core_session(ptr, false))) {
handler(sess);
return switch_status_t.SWITCH_STATUS_SUCCESS;
}
};
return del;
}
public static SWIGTYPE_p_f_p_switch_core_session__switch_status_t WrapStateHandlerDelegate(switch_state_handler_t_delegate del) {
return new SWIGTYPE_p_f_p_switch_core_session__switch_status_t(Marshal.GetFunctionPointerForDelegate(del), false);
}
// These are needed on the ManagedSession bleg, so they don't get GC'd
// while the B Leg is still around
switch_state_handler_t_delegate originate_onhangup_delegate;
switch_state_handler_t_delegate originate_ondestroy_delegate;
switch_state_handler_table originate_table;
GCHandle originate_keepalive_handle; // Make sure the B Leg is not collected and disposed until we run ondestroy
switch_status_t originate_onhangup_method(IntPtr channelPtr) {
// CS_DESTROY lets the bleg be collected
// and frees originate_table memory
// Note that this (bleg ManagedSession) is invalid
// to touch right now - the unmanaged memory has already been free'd
if (this.originate_keepalive_handle.IsAllocated) {
this.originate_keepalive_handle.Free(); // GC can now collect this bleg
}
if (this.originate_table != null) {
this.originate_table.Dispose();
this.originate_table = null;
}
return switch_status_t.SWITCH_STATUS_SUCCESS;
}
/// <summary>
/// Performs originate. Returns ManagedSession on success, null on failure.
/// onHangup is called as a state handler, after the channel is truly hungup.
/// </summary>
public static ManagedSession OriginateHandleHangup(CoreSession aLegSession, string destination, TimeSpan timeout, Action<ManagedSession> onHangup) {
var bleg = new ManagedSession();
bleg.originate_ondestroy_delegate = bleg.originate_onhangup_method;
bleg.originate_onhangup_delegate = CreateStateHandlerDelegate(sess_b => {
if (onHangup != null) {
onHangup(sess_b);
}
});
bleg.originate_table = new switch_state_handler_table();
bleg.originate_table.on_hangup = WrapStateHandlerDelegate(bleg.originate_onhangup_delegate);
bleg.originate_table.on_destroy = WrapStateHandlerDelegate(bleg.originate_ondestroy_delegate);
var res = 0 == bleg.originate(aLegSession, destination, (int)timeout.TotalSeconds, bleg.originate_table);
bleg.originate_keepalive_handle = GCHandle.Alloc(bleg, GCHandleType.Normal); // Prevent GC from eating the bleg
if (res) {
bleg.Initialize();
return bleg;
} else {
// Dispose to free the lock
// The bleg lives on with its unmanaged memory freed
// Until CS_DESTROY gets called
bleg.Dispose();
return null;
}
}
// Convenience // Convenience
public bool IsAvailable { public bool IsAvailable {
get { return this.Ready(); } get { return this.Ready(); }
@ -128,8 +214,16 @@ namespace FreeSWITCH.Native
public Guid Uuid { public Guid Uuid {
get { get {
if (allocated == 0) throw new InvalidOperationException("Session has not been initialized.");
return new Guid(this.GetUuid()); return new Guid(this.GetUuid());
} }
} }
public switch_call_cause_t CallCause {
get {
if (allocated == 0) throw new InvalidOperationException("Session has not been initialized.");
return freeswitch.switch_channel_get_cause(this.channel);
}
}
} }
} }

View File

@ -254,6 +254,7 @@ namespace FreeSWITCH {
ApiExecutors.ForEach(x => x.SetZeroUseNotification(decreaseUnloadCount)); ApiExecutors.ForEach(x => x.SetZeroUseNotification(decreaseUnloadCount));
AppExecutors.ForEach(x => x.SetZeroUseNotification(decreaseUnloadCount)); AppExecutors.ForEach(x => x.SetZeroUseNotification(decreaseUnloadCount));
unloadSignal.WaitOne(); unloadSignal.WaitOne();
GC.WaitForPendingFinalizers();
} }
#endregion #endregion
@ -288,7 +289,7 @@ namespace FreeSWITCH {
// Ensure it's a plugin assembly // Ensure it's a plugin assembly
var ourName = Assembly.GetExecutingAssembly().GetName().Name; var ourName = Assembly.GetExecutingAssembly().GetName().Name;
if (!asm.GetReferencedAssemblies().Any(n => n.Name == ourName)) { if (!asm.GetReferencedAssemblies().Any(n => n.Name == ourName)) {
Log.WriteLine(LogLevel.Debug, "Assembly {0} doesn't reference FreeSWITCH.Managed, not loading."); Log.WriteLine(LogLevel.Debug, "Assembly {0} doesn't reference FreeSWITCH.Managed, not loading.", asm.FullName);
return false; return false;
} }

File diff suppressed because it is too large Load Diff