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

View File

@ -69,7 +69,6 @@ ManagedSession::~ManagedSession()
// Do auto-hangup ourselves because CoreSession can't call check_hangup_hook
// after ManagedSession destruction (cause at point it's pure virtual)
if (session) {
channel = switch_core_session_get_channel(session);
if (switch_test_flag(this, S_HUP) && !switch_channel_test_flag(channel, CF_TRANSFER)) {
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
setAutoHangup(0);
@ -104,3 +103,6 @@ switch_status_t ManagedSession::run_dtmf_callback(void *input, switch_input_type
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 char* (*inputFunction)(void*, switch_input_type_t);
#ifndef _MANAGED
#include <glib.h>
#include <mono/jit/jit.h>
@ -136,7 +135,6 @@ public:
virtual bool begin_allow_threads();
virtual bool end_allow_threads();
virtual void check_hangup_hook();
virtual switch_status_t run_dtmf_callback(void *input, switch_input_type_t itype);
// 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 ***/
const string created_time = "created_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 transfer_time = "transfer_time";

View File

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

View File

@ -43,17 +43,21 @@ namespace FreeSWITCH.Native
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
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
{
// 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);
/// <summary>Initializes the native ManagedSession. Must be called after Originate.</summary>
public void Initialize()
/// <summary>Initializes the native ManagedSession. Called after Originate completes successfully .</summary>
internal void Initialize()
{
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
// 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
public bool IsAvailable {
get { return this.Ready(); }
@ -128,8 +214,16 @@ namespace FreeSWITCH.Native
public Guid Uuid {
get {
if (allocated == 0) throw new InvalidOperationException("Session has not been initialized.");
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));
AppExecutors.ForEach(x => x.SetZeroUseNotification(decreaseUnloadCount));
unloadSignal.WaitOne();
GC.WaitForPendingFinalizers();
}
#endregion
@ -288,7 +289,7 @@ namespace FreeSWITCH {
// Ensure it's a plugin assembly
var ourName = Assembly.GetExecutingAssembly().GetName().Name;
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;
}

File diff suppressed because it is too large Load Diff