From: ram@eiffel.com (Raphael Manfredi)
Newsgroups: comp.lang.eiffel
Subject: Re: CECIL and garbage collection
Summary: New Eiffel-C interface scheme in Eiffel 3
Keywords: memory management, Cecil, garbage collection
Date: 20 Aug 92 16:24:52 GMT
Organization: Interactive Software Engineering, Santa Barbara CA

Quoting seanb@cs.man.ac.uk (Sean Bechhofer) from comp.lang.eiffel:
> 
>        "What keeps the garbage collector from collecting Eiffel
>        objects that are only referenced in C code? Nothing. If
>        garbage collection is enabled, you must keep a reference
>        on the Eiffel side to prevent an object from being collected."
> 
> [deletion]
>
>        o Is this "feature" likely to change in Eiffel 3?

Do you want a short answer or a long answer? Both? Very well, the short answer
is YES. This "bug" has changed in Eiffel 3.

Although in your case the problem manifested itself with the use of Cecil,
it is more general than that, as the quote from the Environment Manual which
you reproduced above would tell us.

In Eiffel 3, whenever the Eiffel side calls an external C routine or whenever
Cecil is requested an object creation, all you get is an EIF_OBJ pointer.
If you wish to get access to the object, you must use the eif_access() macro.
That will give you the physical address of the object, but this pointer is not
to be kept around as the memory manager (aka GC) has entire liberty to move
your objects to optimize the memory usage and reduce fragmentation for instance.
So any access via eif_access() on an EIF_OBJ pointer is safe.

The compiler automatically generates code to protect your references when you
are calling an external C routine. This protection is, by default, released
when Eiffel regains control (but see below). Here is an example:

 --- Eiffel side:

	f (a: GENERAL): GENERAL is
			-- Call to external `f'
		external
			"C"
		end;
	
 --- C side:

#include <cecil.h>

char *f(x)
EIF_OBJ x;
{
	char *pointer = eif_access(x);
	EIF_TYPE_ID general_id;
	EIF_OBJ new_object;

	/* It's safe to keep 'pointer' as long as no Eiffel calls are made, and
	 * no memory allocation is done.
	 */
	
	...

	general_id = eif_type_id("GENERAL");
	new_object = eif_create(general_id);

	some_function(eif_access(new_object), ...);

	/* Eiffel function was declared to return an object, not an EIF_OBJ! */

	return eif_access(new_object);
}

 --- End of example

Now sometimes it is necessary to have a finer control over the memory. For
instance, if Eiffel wishes to call a C library function bcopy(), this function
expects real pointers, not EIF_OBJ pointers! So it is possible to direct the
compiler not to protect references given to the external function by preceding
the variable name with a '$'. The raw reference will then be given to the C.
Should this function call an Eiffel routine or call malloc(), chaos is to be
expected if you ever use the reference you originally got from Eiffel again.

If the C side wants to keep an EIF_OBJ pointer even after the return to Eiffel,
it must issue the run-time call eif_adopt, which will return you a new EIF_OBJ
pointer, owned only by the C side (the original EIF_OBJ will be released when
the control flow returns to the Eiffel routine which issued the protection).
As a side effect, you have the guarantee that this object will be always alive,
even if all the references from Eiffel have been cut. Each time you want to
access the object, you must still use eif_access().

When the C side does not have any more use for an "adopted" EIF_OBJ pointer,
it must call the run-time routine eif_wean(). Any further use of EIF_OBJ is
forbidden once eif_wean() has been called.

If you need to access a given object many times, it is possible to *freeze* its
location in memory by calling eif_freeze(). This run-time call will return you
a real (char *) reference which can be use freely by all the routines. The
object will never move again. In fact, the memory management has not further
control over this object. Even when it is dead, the GC will not collect it.
Therefore, it is up to the programmer to call eif_unfreeze() to let the GC see
the object again and allow it to move and/or be collected.

For more information, you should read "Eiffel: The Language", paragraph 24.7
entitled "The Cecil Library".

To summarize, Eiffel 3 overcomes those memory peculiarities at the cost of a
stronger discipline imposed to the programmer. The scheme is flexible enough
to allow any kind of inter-connection between Eiffel and C. And this is not
even ISE Eiffel-dependent since it is part of the Eiffel specifications which
every Eiffel compiler must meet. However, the ISE Eiffel run-time documentation
will probably mention some over specific low-level run-time calls which bring
much more flexibility, at the price of a lack of portability.
-- 
Raphael Manfredi <ram@eiffel.com>
Interactive Software Engineering Inc.
270 Storke Road, Suite #7                      / Tel +1 (805) 685-1006 \
Goleta, California 93117, USA                  \ Fax +1 (805) 685-6869 /