efl-csharp: fix crash when events trigger after C# object `Dispose`

Summary:
Rework general event handling to check individually each event call, if the
object is not alive then the event will not be propagated.
WeakReferences (and lambdas capturing those WeakRefs) are used to ensure this.

Dispose methods in object now take care of checking if efl libraries are still
initialized and thread-safely unregister each event before performing an
efl_unref on the Eo object.

Event handling in C# is now centered around a single dictionary inside the
object: `EoEvents`.

C# event triggers now properly trigger events on C too.

Standardize C# event-triggering methods names (remove underscores).

Some diminished use of static memory due events no longer requiring static key
objects to be registered/unregistered.

Some fixing of white space generation for generated events.

Depends on D8431

Reviewers: lauromoura, felipealmeida, segfaultxavi

Reviewed By: lauromoura

Subscribers: cedric, #reviewers, #committers

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D8564
This commit is contained in:
Vitor Sousa 2019-04-05 19:59:34 -03:00
parent 1c22a3d819
commit 7c28762f15
7 changed files with 387 additions and 243 deletions

View File

@ -30,7 +30,7 @@ struct unpack_event_args_visitor
// Structs are usually passed by pointer to events, like having a ptr<> modifier // Structs are usually passed by pointer to events, like having a ptr<> modifier
// Uses implicit conversion from IntPtr // Uses implicit conversion from IntPtr
return as_generator( return as_generator(
" evt.Info;" " evt.Info"
).generate(sink, attributes::unused, *context); ).generate(sink, attributes::unused, *context);
} }
else if (type.is_ptr) else if (type.is_ptr)
@ -81,6 +81,116 @@ struct unpack_event_args_visitor
} }
}; };
template<typename OutputIterator, typename Context>
struct pack_event_info_and_call_visitor
{
mutable OutputIterator sink;
Context const* context;
attributes::type_def const& type;
static auto constexpr native_call = "Efl.Eo.Globals.efl_event_callback_call(this.NativeHandle, desc, info);\n";
typedef pack_event_info_and_call_visitor<OutputIterator, Context> visitor_type;
typedef bool result_type;
bool operator()(grammar::attributes::regular_type_def const& regular) const
{
std::string arg_type = name_helpers::type_full_managed_name(regular);
auto const& indent = current_indentation(*context);
if (regular.is_struct())
{
return as_generator(
indent << "IntPtr info = Marshal.AllocHGlobal(Marshal.SizeOf(e.arg));\n"
<< indent << "try\n"
<< indent << "{\n"
<< indent << scope_tab << "Marshal.StructureToPtr(e.arg, info, false);\n"
<< indent << scope_tab << this->native_call
<< indent << "}\n"
<< indent << "finally\n"
<< indent << "{\n"
<< indent << scope_tab << "Marshal.FreeHGlobal(info);\n"
<< indent << "}\n"
).generate(sink, attributes::unused, *context);
}
using attributes::regular_type_def;
struct match
{
eina::optional<std::string> name;
std::function<std::string()> function;
};
std::string full_type_name = name_helpers::type_full_eolian_name(regular);
auto filter_func = [&regular, &full_type_name] (match const& m)
{
return (!m.name || *m.name == regular.base_type || *m.name == full_type_name);
};
match const str_table[] =
{
{"string", [] { return "e.arg"; }}
, {"stringshare", [] { return "e.arg"; }}
};
auto str_accept_func = [&](std::string const& conversion)
{
return as_generator(
indent << "IntPtr info = Eina.StringConversion.ManagedStringToNativeUtf8Alloc(" << conversion << ");\n"
<< indent << "try\n"
<< indent << "{\n"
<< indent << scope_tab << this->native_call
<< indent << "}\n"
<< indent << "finally\n"
<< indent << "{\n"
<< indent << scope_tab << "Eina.MemoryNative.Free(info);\n"
<< indent << "}\n").generate(sink, attributes::unused, *context);
};
if (eina::optional<bool> b = call_match(str_table, filter_func, str_accept_func))
return *b;
match const value_table [] =
{
{"bool", [] { return "e.arg ? (byte) 1 : (byte) 0"; }}
, {"Eina.Error", [] { return "(int)e.arg"; }}
, {nullptr, [] { return "e.arg"; }}
};
auto value_accept_func = [&](std::string const& conversion)
{
return as_generator(
indent << "IntPtr info = Eina.PrimitiveConversion.ManagedToPointerAlloc(" << conversion << ");\n"
<< indent << "try\n"
<< indent << "{\n"
<< indent << scope_tab << this->native_call
<< indent << "}\n"
<< indent << "finally\n"
<< indent << "{\n"
<< indent << scope_tab << "Marshal.FreeHGlobal(info);\n"
<< indent << "}\n").generate(sink, attributes::unused, *context);
};
if (eina::optional<bool> b = call_match(value_table, filter_func, value_accept_func))
return *b;
return value_accept_func("e.args");
}
bool operator()(grammar::attributes::klass_name const&) const
{
auto const& indent = current_indentation(*context);
return as_generator(indent << "IntPtr info = e.arg.NativeHandle;\n"
<< indent << this->native_call).generate(sink, attributes::unused, *context);
}
bool operator()(attributes::complex_type_def const&) const
{
auto const& indent = current_indentation(*context);
return as_generator(indent << "IntPtr info = e.arg.Handle;\n"
<< indent << this->native_call).generate(sink, attributes::unused, *context);
}
};
/* /*
* Generates a struct wrapping the argument of a given event. * Generates a struct wrapping the argument of a given event.
*/ */
@ -138,40 +248,6 @@ struct event_declaration_generator
} }
} const event_declaration {}; } const event_declaration {};
struct event_registration_generator
{
attributes::klass_def const& klass;
attributes::klass_def const& leaf_klass;
bool is_inherited_event;
template<typename OutputIterator, typename Context>
bool generate(OutputIterator sink, attributes::event_def const& evt, Context const& context) const
{
std::string wrapper_event_name;
if (blacklist::is_event_blacklisted(evt, context))
return true;
if (is_inherited_event && !helpers::is_unique_event(evt, leaf_klass))
wrapper_event_name = name_helpers::translate_inherited_event_name(evt, klass);
else
wrapper_event_name = name_helpers::managed_event_name(evt.name);
return as_generator(scope_tab << scope_tab << "evt_" << wrapper_event_name << "_delegate = "
<< "new Efl.EventCb(on_" << wrapper_event_name << "_NativeCallback);\n"
).generate(sink, attributes::unused, context);
}
};
struct event_registration_parameterized
{
event_registration_generator operator()(attributes::klass_def const& klass, attributes::klass_def const& leaf_klass) const
{
bool is_inherited_event = klass != leaf_klass;
return {klass, leaf_klass, is_inherited_event};
}
} const event_registration;
struct event_definition_generator struct event_definition_generator
{ {
attributes::klass_def const& klass; attributes::klass_def const& klass;
@ -185,6 +261,7 @@ struct event_definition_generator
return true; return true;
std::string managed_evt_name = name_helpers::managed_event_name(evt.name); std::string managed_evt_name = name_helpers::managed_event_name(evt.name);
auto const& indent = current_indentation(context);
bool is_unique = helpers::is_unique_event(evt, leaf_klass); bool is_unique = helpers::is_unique_event(evt, leaf_klass);
bool use_explicit_impl = is_inherited_event && !is_unique; bool use_explicit_impl = is_inherited_event && !is_unique;
@ -205,20 +282,35 @@ struct event_definition_generator
std::string wrapper_args_type = "EventArgs"; std::string wrapper_args_type = "EventArgs";
std::string wrapper_args_template = ""; std::string wrapper_args_template = "";
std::string event_args = "EventArgs args = EventArgs.Empty;\n"; std::string event_args = "EventArgs args = EventArgs.Empty;\n";
std::string event_native_call;
efl::eina::optional<grammar::attributes::type_def> etype = evt.type; efl::eina::optional<grammar::attributes::type_def> etype = evt.type;
if (etype.is_engaged()) if (!etype.is_engaged())
{
auto event_call_site_sink = std::back_inserter(event_native_call);
if (!as_generator(indent.inc().inc() << "Efl.Eo.Globals.efl_event_callback_call(this.NativeHandle, desc, IntPtr.Zero);\n")
.generate(event_call_site_sink, attributes::unused, context))
return false;
}
else
{ {
wrapper_args_type = name_helpers::managed_event_args_name(evt); wrapper_args_type = name_helpers::managed_event_args_name(evt);
wrapper_args_template = "<" + wrapper_args_type + ">"; wrapper_args_template = "<" + wrapper_args_type + ">";
std::string arg_initializer = wrapper_args_type + " args = new " + wrapper_args_type + "();\n"; std::string arg_initializer;
arg_initializer += " args.arg = ";
auto arg_initializer_sink = std::back_inserter(arg_initializer); auto arg_initializer_sink = std::back_inserter(arg_initializer);
auto event_call_site_sink = std::back_inserter(event_native_call);
if (!(*etype).original_type.visit(unpack_event_args_visitor<decltype(arg_initializer_sink), Context>{arg_initializer_sink, &context, *etype})) auto sub_context = change_indentation(indent.inc().inc(), context);
if (!as_generator(scope_tab(6) << wrapper_args_type << " args = new " << wrapper_args_type << "();\n"
<< scope_tab(6) << "args.arg = ").generate(arg_initializer_sink, attributes::unused, context))
return false;
if (!(*etype).original_type.visit(unpack_event_args_visitor<decltype(arg_initializer_sink), decltype(sub_context)>{arg_initializer_sink, &sub_context, *etype}))
return false;
if (!(*etype).original_type.visit(pack_event_info_and_call_visitor<decltype(event_call_site_sink), decltype(sub_context)>{event_call_site_sink, &sub_context, *etype}))
return false; return false;
arg_initializer += ";\n"; arg_initializer += ";\n";
@ -226,10 +318,6 @@ struct event_definition_generator
event_args = arg_initializer; event_args = arg_initializer;
} }
if(!as_generator("private static object " << wrapper_evt_name << "Key = new object();\n")
.generate(sink, attributes::unused, context))
return false;
if(!as_generator(documentation(1)).generate(sink, evt, context)) if(!as_generator(documentation(1)).generate(sink, evt, context))
return false; return false;
@ -256,31 +344,10 @@ struct event_definition_generator
return false; return false;
} }
if (!generate_event_add_remove(sink, evt, wrapper_evt_name, context)) if (!generate_event_add_remove(sink, evt, event_args, context))
return false; return false;
if (!generate_event_trigger(sink, wrapper_evt_name, wrapper_args_type, wrapper_args_template, context)) if (!generate_event_trigger(sink, evt, wrapper_evt_name, wrapper_args_type, event_native_call, context))
return false;
// Store the delegate for this event in this instance. This is initialized in RegisterEventProxies()
// We can't initialize them directly here as they depend on the member methods being valid (i.e.
// the constructor being called).
if (!as_generator(scope_tab << "Efl.EventCb evt_" << wrapper_evt_name << "_delegate;\n").generate(sink, attributes::unused, context))
return false;
// Callback to be given to C's callback_priority_add
if (!as_generator(
scope_tab << "private void on_" << wrapper_evt_name << "_NativeCallback(System.IntPtr data, ref Efl.Event.NativeStruct evt)\n"
<< scope_tab << "{\n"
<< scope_tab << scope_tab << event_args
<< scope_tab << scope_tab << "try {\n"
<< scope_tab << scope_tab << scope_tab << "On_" << wrapper_evt_name << "(args);\n"
<< scope_tab << scope_tab << "} catch (Exception e) {\n"
<< scope_tab << scope_tab << scope_tab << "Eina.Log.Error(e.ToString());\n"
<< scope_tab << scope_tab << scope_tab << "Eina.Error.Set(Eina.Error.UNHANDLED_EXCEPTION);\n"
<< scope_tab << scope_tab << "}\n"
<< scope_tab << "}\n\n"
).generate(sink, attributes::unused, context))
return false; return false;
return true; return true;
@ -288,21 +355,26 @@ struct event_definition_generator
template<typename OutputIterator, typename Context> template<typename OutputIterator, typename Context>
bool generate_event_trigger(OutputIterator sink bool generate_event_trigger(OutputIterator sink
, attributes::event_def const &evt
, std::string const& event_name , std::string const& event_name
, std::string const& event_args_type , std::string const& event_args_type
, std::string const& event_template_args , std::string const& event_native_call
, Context const& context) const , Context const& context) const
{ {
auto delegate_type = "EventHandler" + event_template_args; auto library_name = context_find_tag<library_context>(context).actual_library_name(klass.filename);
std::string upper_c_name = utils::to_uppercase(evt.c_name);
if (!as_generator( if (!as_generator(
scope_tab << "///<summary>Method to raise event "<< event_name << ".</summary>\n" scope_tab << "///<summary>Method to raise event "<< event_name << ".</summary>\n"
<< scope_tab << "public void On_" << event_name << "(" << event_args_type << " e)\n" << scope_tab << "public void On" << event_name << "(" << event_args_type << " e)\n"
<< scope_tab << "{\n" << scope_tab << "{\n"
<< scope_tab << scope_tab << delegate_type << " evt;\n" << scope_tab << scope_tab << "var key = \"_" << upper_c_name << "\";\n"
<< scope_tab << scope_tab << "lock (eventLock) {\n" << scope_tab << scope_tab << "IntPtr desc = Efl.EventDescription.GetNative(" << library_name << ", key);\n"
<< scope_tab << scope_tab << "evt = (" << delegate_type << ")eventHandlers[" << event_name << "Key];\n" << scope_tab << scope_tab << "if (desc == IntPtr.Zero)\n"
<< scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << "evt?.Invoke(this, e);\n" << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to get native event {key}\");\n"
<< scope_tab << scope_tab << scope_tab << "return;\n"
<< scope_tab << scope_tab << "}\n\n"
<< event_native_call
<< scope_tab << "}\n" << scope_tab << "}\n"
).generate(sink, nullptr, context)) ).generate(sink, nullptr, context))
return false; return false;
@ -311,7 +383,10 @@ struct event_definition_generator
} }
template<typename OutputIterator, typename Context> template<typename OutputIterator, typename Context>
bool generate_event_add_remove(OutputIterator sink, attributes::event_def const &evt, const std::string& event_name, Context const& context) const bool generate_event_add_remove(OutputIterator sink
, attributes::event_def const &evt
, std::string const& event_args
, Context const& context) const
{ {
std::string upper_c_name = utils::to_uppercase(evt.c_name); std::string upper_c_name = utils::to_uppercase(evt.c_name);
auto unit = (const Eolian_Unit*) context_find_tag<eolian_state_context>(context).state; auto unit = (const Eolian_Unit*) context_find_tag<eolian_state_context>(context).state;
@ -319,22 +394,38 @@ struct event_definition_generator
auto library_name = context_find_tag<library_context>(context).actual_library_name(klass.filename); auto library_name = context_find_tag<library_context>(context).actual_library_name(klass.filename);
return as_generator( return as_generator(
scope_tab << "{\n" scope_tab << "{\n"
<< scope_tab << scope_tab << "add {\n" << scope_tab << scope_tab << "add\n"
<< scope_tab << scope_tab << scope_tab << "lock (eventLock) {\n" << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << "lock (eventLock)\n"
<< scope_tab << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "var wRef = new WeakReference(this);\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "Efl.EventCb callerCb = (IntPtr data, ref Efl.Event.NativeStruct evt) =>\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "var obj = wRef.Target as Efl.Eo.IWrapper;\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "if (obj != null)\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << event_args
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "try\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "value?.Invoke(obj, args);\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "}\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "catch (Exception e)\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "Eina.Log.Error(e.ToString());\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "Eina.Error.Set(Eina.Error.UNHANDLED_EXCEPTION);\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "}\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "}\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "};\n\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "string key = \"_" << upper_c_name << "\";\n" << scope_tab << scope_tab << scope_tab << scope_tab << "string key = \"_" << upper_c_name << "\";\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "if (AddNativeEventHandler(" << library_name << ", key, this.evt_" << event_name << "_delegate)) {\n" << scope_tab << scope_tab << scope_tab << scope_tab << "AddNativeEventHandler(" << library_name << ", key, callerCb, value);\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "eventHandlers.AddHandler(" << event_name << "Key , value);\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "} else\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Error adding proxy for event {key}\");\n"
<< scope_tab << scope_tab << scope_tab << "}\n" // End of lock block << scope_tab << scope_tab << scope_tab << "}\n" // End of lock block
<< scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << "}\n\n"
<< scope_tab << scope_tab << "remove {\n" << scope_tab << scope_tab << "remove\n"
<< scope_tab << scope_tab << scope_tab << "lock (eventLock) {\n" << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << "lock (eventLock)\n"
<< scope_tab << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "string key = \"_" << upper_c_name << "\";\n" << scope_tab << scope_tab << scope_tab << scope_tab << "string key = \"_" << upper_c_name << "\";\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "if (RemoveNativeEventHandler(key, this.evt_" << event_name << "_delegate)) { \n" << scope_tab << scope_tab << scope_tab << scope_tab << "RemoveNativeEventHandler(" << library_name << ", key, value);\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "eventHandlers.RemoveHandler(" << event_name << "Key , value);\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "} else\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Error removing proxy for event {key}\");\n"
<< scope_tab << scope_tab << scope_tab << "}\n" // End of lock block << scope_tab << scope_tab << scope_tab << "}\n" // End of lock block
<< scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << "}\n"
<< scope_tab << "}\n" << scope_tab << "}\n"
@ -365,13 +456,6 @@ struct is_eager_generator<struct ::eolian_mono::event_declaration_generator> : s
template <> template <>
struct is_generator<struct ::eolian_mono::event_declaration_generator> : std::true_type {}; struct is_generator<struct ::eolian_mono::event_declaration_generator> : std::true_type {};
template <>
struct is_eager_generator<struct ::eolian_mono::event_registration_generator> : std::true_type {};
template <>
struct is_generator<struct ::eolian_mono::event_registration_generator> : std::true_type {};
template <>
struct is_generator<struct ::eolian_mono::event_registration_parameterized> : std::true_type {};
template <> template <>
struct is_eager_generator<struct ::eolian_mono::event_definition_generator> : std::true_type {}; struct is_eager_generator<struct ::eolian_mono::event_definition_generator> : std::true_type {};
template <> template <>
@ -385,10 +469,6 @@ struct attributes_needed<struct ::eolian_mono::event_argument_wrapper_generator>
template <> template <>
struct attributes_needed<struct ::eolian_mono::event_declaration_generator> : std::integral_constant<int, 1> {}; struct attributes_needed<struct ::eolian_mono::event_declaration_generator> : std::integral_constant<int, 1> {};
template <> template <>
struct attributes_needed<struct ::eolian_mono::event_registration_generator> : std::integral_constant<int, 1> {};
template <>
struct attributes_needed<struct ::eolian_mono::event_registration_parameterized> : std::integral_constant<int, 1> {};
template <>
struct attributes_needed<struct ::eolian_mono::event_definition_generator> : std::integral_constant<int, 1> {}; struct attributes_needed<struct ::eolian_mono::event_definition_generator> : std::integral_constant<int, 1> {};
template <> template <>
struct attributes_needed<struct ::eolian_mono::event_definition_parameterized> : std::integral_constant<int, 1> {}; struct attributes_needed<struct ::eolian_mono::event_definition_parameterized> : std::integral_constant<int, 1> {};

View File

@ -223,7 +223,6 @@ struct klass
<< scope_tab << "private " << concrete_name << "(System.IntPtr raw)" << (root ? "" : " : base(raw)") << "\n" << scope_tab << "private " << concrete_name << "(System.IntPtr raw)" << (root ? "" : " : base(raw)") << "\n"
<< scope_tab << "{\n" << scope_tab << "{\n"
<< scope_tab << scope_tab << (root ? "handle = raw;\n" : "") << scope_tab << scope_tab << (root ? "handle = raw;\n" : "")
<< scope_tab << scope_tab << "RegisterEventProxies();\n"
<< scope_tab << "}\n" << scope_tab << "}\n"
) )
.generate(sink, attributes::unused, concrete_cxt)) .generate(sink, attributes::unused, concrete_cxt))
@ -238,9 +237,6 @@ struct klass
if (!generate_events(sink, cls, concrete_cxt)) if (!generate_events(sink, cls, concrete_cxt))
return false; return false;
if (!generate_events_registration(sink, cls, concrete_cxt))
return false;
// Parts // Parts
if(!as_generator(*(part_definition)) if(!as_generator(*(part_definition))
.generate(sink, cls.parts, concrete_cxt)) return false; .generate(sink, cls.parts, concrete_cxt)) return false;
@ -316,9 +312,6 @@ struct klass
if (!generate_events(sink, cls, inherit_cxt)) if (!generate_events(sink, cls, inherit_cxt))
return false; return false;
if (!generate_events_registration(sink, cls, inherit_cxt))
return false;
// Parts // Parts
if(!as_generator(*(part_definition)) if(!as_generator(*(part_definition))
.generate(sink, cls.parts, inherit_cxt)) return false; .generate(sink, cls.parts, inherit_cxt)) return false;
@ -459,7 +452,9 @@ struct klass
return true; return true;
if (cls.get_all_events().size() > 0) if (cls.get_all_events().size() > 0)
if (!as_generator(scope_tab << (is_inherit ? "protected " : "private ") << "EventHandlerList eventHandlers = new EventHandlerList();\n").generate(sink, attributes::unused, context)) if (!as_generator(scope_tab << visibility << "Dictionary<(IntPtr desc, object evtDelegate), (IntPtr evtCallerPtr, Efl.EventCb evtCaller)> eoEvents = new Dictionary<(IntPtr desc, object evtDelegate), (IntPtr evtCallerPtr, Efl.EventCb evtCaller)>();\n"
<< scope_tab << visibility << "readonly object eventLock = new object();\n")
.generate(sink, attributes::unused, context))
return false; return false;
if (is_inherit) if (is_inherit)
@ -517,7 +512,6 @@ struct klass
<< scope_tab << "protected " << inherit_name << "(System.IntPtr raw)" << (root ? "" : " : base(raw)") << "\n" << scope_tab << "protected " << inherit_name << "(System.IntPtr raw)" << (root ? "" : " : base(raw)") << "\n"
<< scope_tab << "{\n" << scope_tab << "{\n"
<< scope_tab << scope_tab << (root ? "handle = raw;\n" : "") << scope_tab << scope_tab << (root ? "handle = raw;\n" : "")
<< scope_tab << scope_tab << "RegisterEventProxies();\n"
<< scope_tab << "}\n" << scope_tab << "}\n"
).generate(sink, std::make_tuple(constructors, constructors, constructors), context)) ).generate(sink, std::make_tuple(constructors, constructors, constructors), context))
return false; return false;
@ -560,7 +554,6 @@ struct klass
<< scope_tab << scope_tab << scope_tab << "actual_klass = Efl.Eo.ClassRegister.GetInheritKlassOrRegister(base_klass, ((object)this).GetType());\n" << scope_tab << scope_tab << scope_tab << "actual_klass = Efl.Eo.ClassRegister.GetInheritKlassOrRegister(base_klass, ((object)this).GetType());\n"
<< scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << "}\n"
<< scope_tab << scope_tab << "handle = Efl.Eo.Globals.instantiate_start(actual_klass, parent);\n" << scope_tab << scope_tab << "handle = Efl.Eo.Globals.instantiate_start(actual_klass, parent);\n"
<< scope_tab << scope_tab << "RegisterEventProxies();\n"
<< scope_tab << scope_tab << "if (inherited)\n" << scope_tab << scope_tab << "if (inherited)\n"
<< scope_tab << scope_tab << "{\n" << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.PrivateDataSet(this);\n" << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.PrivateDataSet(this);\n"
@ -580,7 +573,6 @@ struct klass
template <typename OutputIterator, typename Context> template <typename OutputIterator, typename Context>
bool generate_dispose_methods(OutputIterator sink, attributes::klass_def const& cls, Context const& context) const bool generate_dispose_methods(OutputIterator sink, attributes::klass_def const& cls, Context const& context) const
{ {
std::string name = join_namespaces(cls.namespaces, '.') + cls.eolian_name;
if (helpers::has_regular_ancestor(cls)) if (helpers::has_regular_ancestor(cls))
return true; return true;
@ -588,92 +580,64 @@ struct klass
auto inherit_name = name_helpers::klass_concrete_name(cls); auto inherit_name = name_helpers::klass_concrete_name(cls);
std::string events_gchandle;
if (cls.get_all_events().size() > 0)
{
auto events_gchandle_sink = std::back_inserter(events_gchandle);
if (!as_generator(scope_tab << scope_tab << scope_tab << "if (eoEvents.Count != 0)\n"
<< scope_tab << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "GCHandle gcHandle = GCHandle.Alloc(eoEvents);\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "gcHandlePtr = GCHandle.ToIntPtr(gcHandle);\n"
<< scope_tab << scope_tab << scope_tab << "}\n\n")
.generate(events_gchandle_sink, attributes::unused, context))
return false;
}
return as_generator( return as_generator(
scope_tab << "///<summary>Destructor.</summary>\n" scope_tab << "///<summary>Destructor.</summary>\n"
<< scope_tab << "~" << inherit_name << "()\n" << scope_tab << "~" << inherit_name << "()\n"
<< scope_tab << "{\n" << scope_tab << "{\n"
<< scope_tab << scope_tab << "Dispose(false);\n" << scope_tab << scope_tab << "Dispose(false);\n"
<< scope_tab << "}\n" << scope_tab << "}\n\n"
<< scope_tab << "///<summary>Releases the underlying native instance.</summary>\n" << scope_tab << "///<summary>Releases the underlying native instance.</summary>\n"
<< scope_tab << visibility << "void Dispose(bool disposing)\n" << scope_tab << visibility << "void Dispose(bool disposing)\n"
<< scope_tab << "{\n" << scope_tab << "{\n"
<< scope_tab << scope_tab << "if (handle != System.IntPtr.Zero) {\n" << scope_tab << scope_tab << "if (handle != System.IntPtr.Zero)\n"
<< scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << "IntPtr h = handle;\n"
<< scope_tab << scope_tab << scope_tab << "handle = IntPtr.Zero;\n\n"
<< scope_tab << scope_tab << scope_tab << "IntPtr gcHandlePtr = IntPtr.Zero;\n"
<< events_gchandle
<< scope_tab << scope_tab << scope_tab << "if (disposing)\n" << scope_tab << scope_tab << scope_tab << "if (disposing)\n"
<< scope_tab << scope_tab << scope_tab << "{\n" << scope_tab << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.efl_unref(handle);\n" << scope_tab << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.efl_mono_native_dispose(h, gcHandlePtr);\n"
<< scope_tab << scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << scope_tab << "}\n"
<< scope_tab << scope_tab << scope_tab << "else\n" << scope_tab << scope_tab << scope_tab << "else\n"
<< scope_tab << scope_tab << scope_tab << "{\n" << scope_tab << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.efl_mono_thread_safe_efl_unref(handle);\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "Monitor.Enter(Efl.Eo.Config.InitLock);\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "if (Efl.Eo.Config.Initialized)\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.efl_mono_thread_safe_native_dispose(h, gcHandlePtr);\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "}\n\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "Monitor.Exit(Efl.Eo.Config.InitLock);\n"
<< scope_tab << scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << scope_tab << "}\n"
<< scope_tab << scope_tab << scope_tab << "handle = System.IntPtr.Zero;\n"
<< scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << "}\n"
<< scope_tab << "}\n" << scope_tab << "}\n\n"
<< scope_tab << "///<summary>Releases the underlying native instance.</summary>\n" << scope_tab << "///<summary>Releases the underlying native instance.</summary>\n"
<< scope_tab << "public void Dispose()\n" << scope_tab << "public void Dispose()\n"
<< scope_tab << "{\n" << scope_tab << "{\n"
<< scope_tab << scope_tab << "Dispose(true);\n" << scope_tab << scope_tab << "Dispose(true);\n"
<< scope_tab << scope_tab << "GC.SuppressFinalize(this);\n" << scope_tab << scope_tab << "GC.SuppressFinalize(this);\n"
<< scope_tab << "}\n" << scope_tab << "}\n\n"
).generate(sink, attributes::unused, context); ).generate(sink, attributes::unused, context);
} }
template <typename OutputIterator, typename Context>
bool generate_events_registration(OutputIterator sink, attributes::klass_def const& cls, Context const& context) const
{
bool root = !helpers::has_regular_ancestor(cls);
std::string virtual_modifier = " ";
if (!root)
virtual_modifier = "override ";
else
{
if (is_inherit_context(context))
virtual_modifier = "virtual ";
}
// Event proxy registration
if (!as_generator(
scope_tab << "///<summary>Register the Eo event wrappers making the bridge to C# events. Internal usage only.</summary>\n"
<< scope_tab << (is_inherit_context(context) || !root ? "protected " : "") << virtual_modifier << "void RegisterEventProxies()\n"
<< scope_tab << "{\n"
)
.generate(sink, NULL, context))
return false;
// Generate event registrations here
if (!root)
if (!as_generator(scope_tab << scope_tab << "base.RegisterEventProxies();\n").generate(sink, NULL, context))
return false;
// Assigning the delegates
if (!as_generator(*(event_registration(cls, cls))).generate(sink, cls.events, context))
return false;
for (auto&& c : helpers::non_implemented_interfaces(cls, context))
{
// Only non-regular types (which declare events through interfaces) need to register them.
if (c.type == attributes::class_type::regular)
continue;
attributes::klass_def klass(get_klass(c, cls.unit), cls.unit);
if (!as_generator(*(event_registration(klass, cls))).generate(sink, klass.events, context))
return false;
}
if (!as_generator(
scope_tab << "}\n"
).generate(sink, NULL, context))
return false;
return true;
}
template <typename OutputIterator, typename Context> template <typename OutputIterator, typename Context>
bool generate_events(OutputIterator sink, attributes::klass_def const& cls, Context const& context) const bool generate_events(OutputIterator sink, attributes::klass_def const& cls, Context const& context) const
{ {
@ -685,69 +649,70 @@ struct klass
if (!helpers::has_regular_ancestor(cls)) if (!helpers::has_regular_ancestor(cls))
{ {
if (!as_generator(scope_tab << visibility << "readonly object eventLock = new object();\n"
<< scope_tab << visibility << "Dictionary<string, int> event_cb_count = new Dictionary<string, int>();\n")
.generate(sink, NULL, context))
return false;
// Callback registration functions // Callback registration functions
if (!as_generator( if (!as_generator(
scope_tab << "///<summary>Adds a new event handler, registering it to the native event. For internal use only.</summary>\n" scope_tab << "///<summary>Adds a new event handler, registering it to the native event. For internal use only.</summary>\n"
<< scope_tab << "///<param name=\"lib\">The name of the native library definining the event.</param>\n" << scope_tab << "///<param name=\"lib\">The name of the native library definining the event.</param>\n"
<< scope_tab << "///<param name=\"key\">The name of the native event.</param>\n" << scope_tab << "///<param name=\"key\">The name of the native event.</param>\n"
<< scope_tab << "///<param name=\"evt_delegate\">The delegate to be called on event raising.</param>\n" << scope_tab << "///<param name=\"evtCaller\">Delegate to be called by native code on event raising.</param>\n"
<< scope_tab << "///<returns>True if the delegate was successfully registered.</returns>\n" << scope_tab << "///<param name=\"evtDelegate\">Managed delegate that will be called by evtCaller on event raising.</param>\n"
<< scope_tab << visibility << "bool AddNativeEventHandler(string lib, string key, Efl.EventCb evt_delegate) {\n" << scope_tab << visibility << "void AddNativeEventHandler(string lib, string key, Efl.EventCb evtCaller, object evtDelegate)\n"
<< scope_tab << scope_tab << "int event_count = 0;\n" << scope_tab << "{\n"
<< scope_tab << scope_tab << "if (!event_cb_count.TryGetValue(key, out event_count))\n"
<< scope_tab << scope_tab << scope_tab << "event_cb_count[key] = event_count;\n"
<< scope_tab << scope_tab << "if (event_count == 0) {\n"
<< scope_tab << scope_tab << scope_tab << "IntPtr desc = Efl.EventDescription.GetNative(lib, key);\n" << scope_tab << scope_tab << "IntPtr desc = Efl.EventDescription.GetNative(lib, key);\n"
<< scope_tab << scope_tab << scope_tab << "if (desc == IntPtr.Zero) {\n" << scope_tab << scope_tab << "if (desc == IntPtr.Zero)\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to get native event {key}\");\n" << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "return false;\n" << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to get native event {key}\");\n"
<< scope_tab << scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << "}\n\n"
<< scope_tab << scope_tab << "if (eoEvents.ContainsKey((desc, evtDelegate)))\n"
<< scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << "Eina.Log.Warning($\"Event proxy for event {key} already registered!\");\n"
<< scope_tab << scope_tab << scope_tab << "return;\n"
<< scope_tab << scope_tab << "}\n\n"
<< scope_tab << scope_tab << "IntPtr evtCallerPtr = Marshal.GetFunctionPointerForDelegate(evtCaller);\n"
<< scope_tab << scope_tab << "if (!Efl.Eo.Globals.efl_event_callback_priority_add(handle, desc, 0, evtCallerPtr, IntPtr.Zero))\n"
<< scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to add event proxy for event {key}\");\n"
<< scope_tab << scope_tab << scope_tab << "return;\n"
<< scope_tab << scope_tab << "}\n\n"
<< scope_tab << scope_tab << "eoEvents[(desc, evtDelegate)] = (evtCallerPtr, evtCaller);\n"
<< scope_tab << scope_tab << "Eina.Error.RaiseIfUnhandledException();\n"
<< scope_tab << "}\n\n"
<< scope_tab << scope_tab << scope_tab << " bool result = Efl.Eo.Globals.efl_event_callback_priority_add(handle, desc, 0, evt_delegate, System.IntPtr.Zero);\n"
<< scope_tab << scope_tab << scope_tab << "if (!result) {\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to add event proxy for event {key}\");\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "return false;\n"
<< scope_tab << scope_tab << scope_tab << "}\n"
<< scope_tab << scope_tab << scope_tab << "Eina.Error.RaiseIfUnhandledException();\n"
<< scope_tab << scope_tab << "} \n"
<< scope_tab << scope_tab << "event_cb_count[key]++;\n"
<< scope_tab << scope_tab << "return true;\n"
<< scope_tab << "}\n"
<< scope_tab << "///<summary>Removes the given event handler for the given event. For internal use only.</summary>\n" << scope_tab << "///<summary>Removes the given event handler for the given event. For internal use only.</summary>\n"
<< scope_tab << "///<param name=\"lib\">The name of the native library definining the event.</param>\n"
<< scope_tab << "///<param name=\"key\">The name of the native event.</param>\n" << scope_tab << "///<param name=\"key\">The name of the native event.</param>\n"
<< scope_tab << "///<param name=\"evt_delegate\">The delegate to be removed.</param>\n" << scope_tab << "///<param name=\"evtDelegate\">The delegate to be removed.</param>\n"
<< scope_tab << "///<returns>True if the delegate was successfully registered.</returns>\n" << scope_tab << visibility << "void RemoveNativeEventHandler(string lib, string key, object evtDelegate)\n"
<< scope_tab << visibility << "bool RemoveNativeEventHandler(string key, Efl.EventCb evt_delegate) {\n" << scope_tab << "{\n"
<< scope_tab << scope_tab << "int event_count = 0;\n"
<< scope_tab << scope_tab << "if (!event_cb_count.TryGetValue(key, out event_count))\n"
<< scope_tab << scope_tab << scope_tab << "event_cb_count[key] = event_count;\n"
<< scope_tab << scope_tab << "if (event_count == 1) {\n"
<< scope_tab << scope_tab << scope_tab << "IntPtr desc = Efl.EventDescription.GetNative(" << scope_tab << scope_tab << "IntPtr desc = Efl.EventDescription.GetNative(lib, key);\n"
<< context_find_tag<library_context>(context).actual_library_name(cls.filename) << ", key);\n" << scope_tab << scope_tab << "if (desc == IntPtr.Zero)\n"
<< scope_tab << scope_tab << scope_tab << "if (desc == IntPtr.Zero) {\n" << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to get native event {key}\");\n" << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to get native event {key}\");\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "return false;\n" << scope_tab << scope_tab << scope_tab << "return;\n"
<< scope_tab << scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << "}\n\n"
<< scope_tab << scope_tab << scope_tab << "bool result = Efl.Eo.Globals.efl_event_callback_del(handle, desc, evt_delegate, System.IntPtr.Zero);\n" << scope_tab << scope_tab << "var evtPair = (desc, evtDelegate);\n"
<< scope_tab << scope_tab << scope_tab << "if (!result) {\n" << scope_tab << scope_tab << "if (eoEvents.TryGetValue(evtPair, out var caller))\n"
<< scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << "if (!Efl.Eo.Globals.efl_event_callback_del(handle, desc, caller.evtCallerPtr, IntPtr.Zero))\n"
<< scope_tab << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to remove event proxy for event {key}\");\n" << scope_tab << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to remove event proxy for event {key}\");\n"
<< scope_tab << scope_tab << scope_tab << scope_tab << "return false;\n" << scope_tab << scope_tab << scope_tab << scope_tab << "return;\n"
<< scope_tab << scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << scope_tab << "}\n\n"
<< scope_tab << scope_tab << scope_tab << "eoEvents.Remove(evtPair);\n"
<< scope_tab << scope_tab << scope_tab << "Eina.Error.RaiseIfUnhandledException();\n" << scope_tab << scope_tab << scope_tab << "Eina.Error.RaiseIfUnhandledException();\n"
<< scope_tab << scope_tab << "} else if (event_count == 0) {\n" << scope_tab << scope_tab << "}\n"
<< scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Trying to remove proxy for event {key} when there is nothing registered.\");\n" << scope_tab << scope_tab << "else\n"
<< scope_tab << scope_tab << scope_tab << "return false;\n" << scope_tab << scope_tab << "{\n"
<< scope_tab << scope_tab << "} \n" << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Trying to remove proxy for event {key} when it is nothing registered.\");\n"
<< scope_tab << scope_tab << "event_cb_count[key]--;\n" << scope_tab << scope_tab << "}\n"
<< scope_tab << scope_tab << "return true;\n"
<< scope_tab << "}\n" << scope_tab << "}\n"
) )
.generate(sink, NULL, context)) .generate(sink, NULL, context))

View File

@ -134,6 +134,7 @@ run(options_type const& opts)
"using System.Runtime.InteropServices;\n" "using System.Runtime.InteropServices;\n"
"using System.Collections.Generic;\n" "using System.Collections.Generic;\n"
"using System.Linq;\n" "using System.Linq;\n"
"using System.Threading;\n"
"using System.ComponentModel;\n") "using System.ComponentModel;\n")
.generate(iterator, efl::eolian::grammar::attributes::unused, efl::eolian::grammar::context_null())) .generate(iterator, efl::eolian::grammar::attributes::unused, efl::eolian::grammar::context_null()))
{ {

View File

@ -67,6 +67,12 @@ public class Globals
efl_ref_count_delegate(IntPtr eo); efl_ref_count_delegate(IntPtr eo);
[DllImport(efl.Libs.Eo)] public static extern int [DllImport(efl.Libs.Eo)] public static extern int
efl_ref_count(IntPtr eo); efl_ref_count(IntPtr eo);
[DllImport(efl.Libs.CustomExports)] public static extern void
efl_mono_gchandle_callbacks_set(Efl.FreeGCHandleCb freeGCHandleCb, Efl.RemoveEventsCb removeEventsCb);
[DllImport(efl.Libs.CustomExports)] public static extern void
efl_mono_native_dispose(IntPtr eo, IntPtr gcHandle);
[DllImport(efl.Libs.CustomExports)] public static extern void
efl_mono_thread_safe_native_dispose(IntPtr eo, IntPtr gcHandle);
[DllImport(efl.Libs.CustomExports)] public static extern void [DllImport(efl.Libs.CustomExports)] public static extern void
efl_mono_thread_safe_efl_unref(IntPtr eo); efl_mono_thread_safe_efl_unref(IntPtr eo);
@ -186,28 +192,14 @@ public class Globals
public delegate IntPtr dlerror_delegate(); public delegate IntPtr dlerror_delegate();
[DllImport(efl.Libs.Evil)] public static extern IntPtr dlerror(); [DllImport(efl.Libs.Evil)] public static extern IntPtr dlerror();
public delegate bool efl_event_callback_priority_add_delegate( [DllImport(efl.Libs.Eo)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool
System.IntPtr obj, efl_event_callback_priority_add(IntPtr obj, IntPtr desc, short priority, IntPtr cb, IntPtr data);
IntPtr desc,
short priority, [DllImport(efl.Libs.Eo)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool
Efl.EventCb cb, efl_event_callback_del(IntPtr obj, IntPtr desc, IntPtr cb, IntPtr data);
System.IntPtr data);
[DllImport(efl.Libs.Eo)] public static extern bool efl_event_callback_priority_add( [DllImport(efl.Libs.Eo)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool
System.IntPtr obj, efl_event_callback_call(IntPtr obj, IntPtr desc, IntPtr event_info);
IntPtr desc,
short priority,
Efl.EventCb cb,
System.IntPtr data);
public delegate bool efl_event_callback_del_delegate(
System.IntPtr obj,
IntPtr desc,
Efl.EventCb cb,
System.IntPtr data);
[DllImport(efl.Libs.Eo)] public static extern bool efl_event_callback_del(
System.IntPtr obj,
IntPtr desc,
Efl.EventCb cb,
System.IntPtr data);
public const int RTLD_NOW = 2; public const int RTLD_NOW = 2;
@ -625,17 +617,79 @@ public class Globals
return ret; return ret;
} }
public static void FreeGCHandleCallback(IntPtr gcHandlePtr)
{
try
{
GCHandle gcHandle = GCHandle.FromIntPtr(gcHandlePtr);
gcHandle.Free();
}
catch (Exception e)
{
Eina.Log.Error(e.ToString());
Eina.Error.Set(Eina.Error.UNHANDLED_EXCEPTION);
}
}
public static void RemoveEventsCallback(IntPtr obj, IntPtr gcHandlePtr)
{
try
{
GCHandle gcHandle = GCHandle.FromIntPtr(gcHandlePtr);
var eoEvents = gcHandle.Target as Dictionary<(IntPtr desc, object evtDelegate), (IntPtr evtCallerPtr, Efl.EventCb evtCaller)>;
if (eoEvents == null)
{
Eina.Log.Error($"Invalid event dictionary [GCHandle pointer: {gcHandlePtr}]");
return;
}
foreach (var item in eoEvents)
{
if (!efl_event_callback_del(obj, item.Key.desc, item.Value.evtCallerPtr, IntPtr.Zero))
{
Eina.Log.Error($"Failed to remove event proxy for event {item.Key.desc} [cb: {item.Value.evtCallerPtr}]");
}
}
}
catch (Exception e)
{
Eina.Log.Error(e.ToString());
Eina.Error.Set(Eina.Error.UNHANDLED_EXCEPTION);
}
}
public static void SetNativeDisposeCallbacks()
{
efl_mono_gchandle_callbacks_set(FreeGCHandleCallback, RemoveEventsCallback);
}
} // Globals } // Globals
public static class Config public static class Config
{ {
public static bool Initialized {
get;
private set;
}
public static readonly object InitLock = new object();
public static void Init() public static void Init()
{ {
Globals.efl_object_init(); Globals.efl_object_init();
Monitor.Enter(InitLock);
Initialized = true;
Monitor.Exit(InitLock);
Globals.SetNativeDisposeCallbacks();
} }
public static void Shutdown() public static void Shutdown()
{ {
Monitor.Enter(InitLock);
Initialized = false;
Monitor.Exit(InitLock);
Globals.efl_object_shutdown(); Globals.efl_object_shutdown();
} }
} }

View File

@ -115,6 +115,8 @@ public struct EventDescription
}; };
public delegate void EventCb(System.IntPtr data, ref Event.NativeStruct evt); public delegate void EventCb(System.IntPtr data, ref Event.NativeStruct evt);
public delegate void FreeGCHandleCb(System.IntPtr gcHandle);
public delegate void RemoveEventsCb(System.IntPtr obj, System.IntPtr gcHandle);
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct TextCursorCursor public struct TextCursorCursor

View File

@ -23,6 +23,46 @@
# endif # endif
#endif /* ! _WIN32 */ #endif /* ! _WIN32 */
typedef void (*Efl_Mono_Free_GCHandle_Cb)(void *gchandle);
typedef void (*Efl_Mono_Remove_Events_Cb)(Eo *obj, void *gchandle);
static Efl_Mono_Free_GCHandle_Cb _efl_mono_free_gchandle_call = NULL;
static Efl_Mono_Remove_Events_Cb _efl_mono_remove_events_call = NULL;
EAPI void efl_mono_gchandle_callbacks_set(Efl_Mono_Free_GCHandle_Cb free_gchandle_cb, Efl_Mono_Remove_Events_Cb remove_events_cb)
{
_efl_mono_free_gchandle_call = free_gchandle_cb;
_efl_mono_remove_events_call = remove_events_cb;
}
EAPI void efl_mono_native_dispose(Eo *obj, void* gchandle)
{
if (gchandle) _efl_mono_remove_events_call(obj, gchandle);
efl_unref(obj);
if (gchandle) _efl_mono_free_gchandle_call(gchandle);
}
typedef struct _Efl_Mono_Native_Dispose_Data
{
Eo *obj;
void *gchandle;
} Efl_Mono_Native_Dispose_Data;
static void _efl_mono_native_dispose_cb(void *data)
{
Efl_Mono_Native_Dispose_Data *dd = data;
efl_mono_native_dispose(dd->obj, dd->gchandle);
free(dd);
}
EAPI void efl_mono_thread_safe_native_dispose(Eo *obj, void* gchandle)
{
Efl_Mono_Native_Dispose_Data *dd = malloc(sizeof(Efl_Mono_Native_Dispose_Data));
dd->obj = obj;
dd->gchandle = gchandle;
ecore_main_loop_thread_safe_call_async(_efl_mono_native_dispose_cb, dd);
}
static void _efl_mono_unref_cb(void *obj) static void _efl_mono_unref_cb(void *obj)
{ {
efl_unref(obj); efl_unref(obj);

View File

@ -49,6 +49,7 @@ class TestEo
Test.Assert(delEventCalled, "DEL event not called"); Test.Assert(delEventCalled, "DEL event not called");
} */ } */
/* Commented until we figure out a new way to test disposing
public static void dispose_really_frees() public static void dispose_really_frees()
{ {
bool delEventCalled = false; bool delEventCalled = false;
@ -62,6 +63,7 @@ class TestEo
Test.Assert(delEventCalled, "DEL event not called"); Test.Assert(delEventCalled, "DEL event not called");
} }
*/
/* Commented out as adding the event listener seems to prevent it from being GC'd. /* Commented out as adding the event listener seems to prevent it from being GC'd.
public static void derived_destructor_really_frees() public static void derived_destructor_really_frees()