Page MenuHomePhabricator

C#: Use System.Collection instead of Eina classes in C# API
Open, HighPublic

Description

Current approach

Currently C# bindings have explicit Eina collection classes that are used in the API. With them, an Eolian method list<int> method(int a, int b) maps to the following C# method: Eina.List<int> Method(int a, int b).

Proposal

Instead of exposing the Eina API directly, we could replace them in the API with System.Collection.Generic interfaces as in the mapping below. The example above becomes IList<int> Method(int a, int b).

Using such interfaces would hide the C details and allow the user passing collections he's more used to.

Meanwhile, the Eina.* classes could still be exposed but as an optional implementation of the Collections interfaces in case the C# developer wants better performance interacting with the native methods.

var optimized_list = new Eina.List<int>();
var pure_list = new Collections.List<int>();

....

// void SetList(Collections.IList<T> list);
SetList(optimized_list);
SetList(pure_list);

Mapping between Eina classes and System.Collections ones

Eina classSystem.Collection counterpart
Eina.Accessor<T>System.Collections.IEnumerable<T>
Eina.Array<T>System.Collections.IList<T>
Eina.Hash<K,V>System.Collections.IDictionary<K, V>
Eina.Iterator<T>System.Collections.IEnumerable<T>
Eina.List<T>System.Collections.IList<T>

Issues regarding data ownership

Data leak converting pure C# collections

Special attention must be paid to data that require copying and are given back to C *without* @move to avoid resource leak.

For example, a method returning a list<int> without @move. If the user returns a Collection.List<t> it will have to be converted to a Native Eina_List before being passed to C. But as it happens without @move, it could potentially leak in virtual method wrappers when control goes back from C# to C.

This does not affect directly Eina.List wrappers as the C# wrapper controls the ownership of the native list, assuming the C# method returning the list will keep it alive.

Invalidation of managed data in @move methods.

This is an issue that could potentially affect current code. As moving collections into @move methods could potentially free them immediately, passing data as @move into a native function should either:

  1. Invalidate the C# wrapper if it was an Eina.List-like wrapper.
  2. Copy the data into a new native collection to be passed to C, to keep the original list alive.
lauromoura triaged this task as High priority.

Thank you for summary in detail. i also like this idea.

  • I think Eina.Accessor should be replaced with System.Collections.IEnumerable. Eina.Accessor docs said
Accessors provide an uniform way of accessing Eina containers,
similar to C++ STL's and C# IEnumerable.
  • In order to use Eina.List, Eina.Array as IList<T> parameter, they need to implement IList interface.
  • I guess we can handle memory leak.
typec -> c#c# -> c
parameter type with @moveCreate managed type and free unmanaged type.Create unmanaged type. use it as parameter.
parameter type without @moveCreate managed type.Create unmanaged type. use it as parameter. and free unmanaged type.
return type with @moveCreate managed type and free unmanaged type.N/A
return type without @moveCreate managed type.N/A

Thank you for summary in detail. i also like this idea.

  • I think Eina.Accessor should be replaced with System.Collections.IEnumerable. Eina.Accessor docs said ` Accessors provide an uniform way of accessing Eina containers, similar to C++ STL's and C# IEnumerable. `

Yeah, that is an option too. I thought about IList because Eina_Accessor has eina_accessor_get_at which would allow random access.

  • In order to use Eina.List, Eina.Array as IList<T> parameter, they need to implement IList interface.

Sure. I've created two subtasks for them.

  • I guess we can handle memory leak.
typec -> c#c# -> c
parameter type with @moveCreate managed type and free unmanaged type.Create unmanaged type. use it as parameter.
parameter type without @moveCreate managed type.Create unmanaged type. use it as parameter. and free unmanaged type.
return type with @moveCreate managed type and free unmanaged type.N/A
return type without @moveCreate managed type.N/A

This is a good starting point.

lauromoura updated the task description. (Show Details)Mon, Nov 25, 8:50 AM

Changed Accessor<T> mapping to be IEnumerable<T> as both IList<T> and IReadOnlyList<T> require the size of the collection to be known.

I've updated devs/lauromoura/remove_eina_mono (based on the original github branch) with some manual test code fixes so we can focus on the ownership issues.