Protecting objects

by Manu (modified: 2010 Feb 17)

Having looked at code for interfacing Eiffel to C and creating callbacks from C to Eiffel, I frequently found that things were not done properly, resulting in potential memory corruption or crashes.

First, I like to point out the CECIL documentation which contains everything you need to know about interfacing C with Eiffel. It might be overwhelming so below is a simplified tutorial.

Typing

At the C level, Eiffel objects are known under 2 possible types:

  • EIF_REFERENCE: a direct reference to an Eiffel object
  • EIF_OBJECT: an indirect (or protected) reference to an Eiffel object

The reason for having 2 types lies in the garbage collector (GC) which moves objects around at runtime. As a consequence, a direct reference is not always valid, meaning that if you use an EIF_REFERENCE variable, then it might points to the previous location of the object. This is why we have EIF_OBJECT, those variable are automatically updated by the GC. To access the Eiffel objects, one has to use the eif_access function.

Callbacks

The corresponding C signature of an Eiffel routine is the same as the Eiffel routine except that there is an extra first argument for the target object of the call. In other words, the Eiffel codea.f (i)is equivalent to the C codef(a, i)

The types used for the declaration of the C routine are simply the Eiffel types prefixed by `EIF_' for the basic types (INTEGER_XX, NATURAL_XX, POINTER, ....) and EIF_REFERENCE for all the other types. Therefore the following Eiffel routine:my_routine (i: INTEGER_32)has the following C signature:void my_routine (EIF_REFERENCE Current, EIF_INTEGER_32 i)

Passing an Eiffel routine to a C external

Instead of using the CECIL API for getting the address of an Eiffel routine, I'm using an alternative which is much lighter and simpler to use in my opinion. This is the technique used in all the Eiffel Software libraries where a simple callback is needed.

Basically if you want to pass the function pointer of the Eiffel routine my_routine above, you simply have to wrap the following C routine:void set_callback_function (void (*) (EIF_REFERENCE, EIF_INTEGER_32));into an Eiffel routine:set_callback_function (a_routine: POINTER) external "C inline use %"my_c_header.h%"" alias "set_callback_function (($a_routine);" end
And then simply call it and pass the address of the Eiffel routine:set_callback_function ($my_routine)We will suppose for the rest of the discussion that the set_callback_function C routine sets a C variable callback_function.

Passing the object target of the call to C

Now, we need to store the Eiffel objects on the C side. As we have seen above, we need to provide the C side with a protected reference to the Eiffel object, otherwise the reference might not be valid after a GC cycle. So on the C side, we need another C variable eiffel_object which is of type EIF_OBJECT. To pass this object from the Eiffel side to the C side you can simply do the following:set_eiffel_object (a: POINTER) external "C inline use %"my_c_header.h%"" alias "set_eiffel_object (eif_protect($a));" end
which you call it that way:-- Pass `my_object' to the C side. set_eiffel_object ($my_object)

Calling the Eiffel routine from C

Then from the C code, calling the Eiffel routine is just:((void (*) (EIF_REFERENCE, EIF_INTEGER_32)) callback_function) (eif_access(eiffel_object), integer_value);

Comments
  • Colin LeMahieu (8 years ago 12/6/2008)

    When you eif_protect() something in C, is this considered a reachable reference to the GC? i.e. if the Eiffel side of code loses reachability to an object that was passed to C and eif_protect'ed, will it ever be collected?

    • Tao Feng (8 years ago 12/6/2008)

      It won't be collected until eif_wean() is called.