4

In the example erlang port program

tuplep = erl_decode(buf);
fnp = erl_element(1, tuplep);
argp = erl_element(2, tuplep);
...
erl_free_compound(tuplep);
erl_free_term(fnp);
erl_free_term(argp);

Both erl_free_compound and erl_free_term are used for freeing term (and its sub-term) separately of the same ETERM*. From the documentation of erl_free_compund() it says

erl_free_compound() will recursively free all of the sub-terms associated with a given Erlang term

So, my question is, does erl_element() makes a copy of the element which, if not freed separately will leak memory or the above situation might lead to double free which is detected and handled by erl_free_term.

  • Had a quick look into the sources of erl_interface. I have not wrapped my head completely around it yet, but it seems that a reference counting is involved. So, apparently erl_element increases the reference count which is ensured deleted by the following erl_free_term call. Or did miss something? – alienfromouterspace Jun 20 '13 at 23:21

1 Answers1

3

The erl_interface library indeed uses a sort of reference counting system to keep track of allocated ETERM structs. So if you write:

ETERM *t_arr[2]; 
ETERM *t1; 

t_arr[0] = erl_mk_atom("hello"); 
t_arr[1] = erl_mk_atom("world"); 
t1 = erl_mk_tuple(&t_arr[0],2); 

you have created three (3) Erlang terms (ETERM). Now if you call: erl_free_term(t1) you only free upp the tuple not the other two ETERM's. To free all the allocated memory, you would have to call:

erl_free_term(t_arr[0]); 
erl_free_term(t_arr[1]); 
erl_free_term(t1) 

To avoid all these calls to erl_free_term() you can use: erl_free_compund() instead. It does a "deep" free of all ETERM's. So the above could be accomplished with:

erl_free_compund(t1) 

Thus, this routine makes it possible for you to write in a more compact way where you don't have to remember the references to all sub-component ETERM's. Example:

ETERM *list; 

list = erl_cons(erl_mk_int(36), 
erl_cons(erl_mk_atom("tobbe"), 
erl_mk_empty_list())); 
... /* do some work */ 
erl_free_compound(list); 

Update: To check if you really free up all the create terms you can use this piece of code (original manual entry:

long allocated, freed;

erl_eterm_statistics(&allocated,&freed);
printf("currently allocated blocks: %ld\n",allocated);
printf("length of freelist: %ld\n",freed);

/* really free the freelist */
erl_eterm_release();

(answer adopted from here)

Ward Bekker
  • 6,316
  • 9
  • 38
  • 61
  • Thanks for replying @WardBekker. But this doesn't address my original question. I have already [read](http://www.trapexit.org/forum/viewtopic.php?p=837&sid=7a9742f52a7d9cd46cfc6649e00d0049) and searched around before I posted here. There seems to be a conflict in the understanding of this between what is in the doc as I posted and what is discussed in trapexit. – alienfromouterspace Jun 21 '13 at 10:11
  • 1
    @alienfromouterspace Check the addition on my answer. Hopefully it helps. From the code of an Erlang memcached client https://code.google.com/p/erlangmc/source/browse/trunk/c_src/EMCPort.cpp?r=c010b97cd09111ea52d88c5529f39b381b44af74 (assuming it is correct), you can just call `erl_free_compound()`, and don't need to call `erl_free_term()` for every TERM created with `erl_element`. – Ward Bekker Jun 21 '13 at 18:08
  • Thanks for this new line of thought @WardBekker. I am going to write a small piece of C program building and destroying complex erlang terms and measure it using erl_eterm_statistics(). That way I am going to have a better understanding of aparent memory leak scenario work. At least it should help to correct my understanding of ETERM. I will post back my findings. – alienfromouterspace Jun 23 '13 at 20:35