close Warning: Can't synchronize with repository "(default)" (/var/svn/tolp does not appear to be a Subversion repository.). Look in the Trac log for more information.

Opened 14 years ago

Closed 14 years ago

Last modified 14 years ago

#1121 closed defect (fixed)

Not deleted objects using a TimeSet as argument

Reported by: Pedro Gea Owned by: Víctor de Buen Remiro
Priority: high Milestone: Mantainance
Component: Kernel Version: head
Severity: major Keywords:
Cc:

Description

Se encuentran objetos no borrados cuando se utiliza un timeset como argumento de una función.

Véase el siguiente ejemplo que se deja un objeto cada vez:

Real N1 = Copy(NObject);

Real {
  Serie CalIndW(TimeSet dtn) { CalInd(W, dtn) };
  Serie CalIndW(C);
0};

Real N2 = Copy(NObject);
WriteLn(""<<Real (N2-N1-2));

Change History (12)

comment:1 Changed 14 years ago by Víctor de Buen Remiro

Status: newaccepted

No hace falta usar una función para observar el problema, pues este otro código produce el mismo efecto.

Real N1 = Copy(NObject);
TimeSet aux = C;
Serie CalInd(W, aux);
Real N2 = Copy(NObject);
WriteLn(""<<Real (N2-N1-2));

Se trata un bug conocido que nunca he podido resolver, y que se debe a que la serie tiene hace uso del TimeSet en el que descansa de una forma que no está controlada por los mecanismos sintácticos. Es decir, el objeto Serie puede sobrevivir al objeto TimeSet en cuyo caso se produciría un acceso de memoria inválido. Por este motivo, se crea una referencia interna desde la Serie al TimeSet que no se destruye hasta que la Serie es destruida. En realidad no es tan sencillo pero más o menos es eso, hay dos mecanismos referenciales que se mezclan y no es fácil hacerlos convivir pacíficamente.

La recomendación es utilizar un texto como argumento de funciones y evaluarlo internamente con Eval

Real N1 = Copy(NObject);

Real {
  Serie CalIndW(Text dtn) { CalInd(W, Eval(dtn)) };
  Serie CalIndW("C");
0};

Real N2 = Copy(NObject);
WriteLn(""<<Real (N2-N1-2));

Como ha habido bastantes mejoras sobre otros memory leaks puede ser un buen momento para reintentar hacer algo de limpieza por ahí.

comment:2 Changed 14 years ago by Víctor de Buen Remiro

Con respecto al mecanismo de chequeo en TOL de código sospechoso de memory leaks hay una forma de evitar efectos secundarios en el conteo de objetos, que es crear todos los objetos auxiliares al principio y luego modificarlos con := de forma que el balance tenga que ser siempre cero:

//Creación de variables
Real initial_objects = ?;
Real lost_objects = ?;
Real aux = ?;

//Conteo inicial
Real initial_objects := Copy(NObject);

Real  aux := 
{
  //Código de pruebas susceptible de pérdida de objetos
  0
};

//Recuento final
Real lost_objects := Copy(NObject) - initial_objects;

WriteLn("lost_objects = "<< lost_objects );

Otras veces el problema puede aparecer al repetir varias veces cierto código, o por el contrario sólo perderse memoria en la primera iteración y no en las siguientes. Por ello otra forma de chequeo bastante recomendable es la siguiente:

//Creación de variables
Real initial_objects = ?;
Real lost_objects = ?;
Real iter = 1;
Real maxIter = 3;

//Conteo inicial
Real initial_objects := Copy(NObject);

//Encapsulación del código
Real While(iter<=maxIter, 
{
  //Código de pruebas susceptible de pérdida de objetos
  iter := iter+1
});

//Recuento final
Real lost_objects := Copy(NObject) - initial_objects;

WriteLn("lost objects = "<< lost_objects+" in "<<maxIter+" iterations" );
WriteLn("lost objects by iteration= "<< (lost_objects/maxIter) );

comment:3 Changed 14 years ago by Víctor de Buen Remiro

Es posible incluso crear una función de chequeo estandarizado

////////////////////////////////////////////////////////////////////////////////
//Chequea si cierto código TOL se deja objetos sin borrar en un ciclo de 
//longitud dada
Real check_memory_leak_in_cycle(
  Real maxIter,   //Número de iteraciones
  Code code )     //Función de API Real(Real void) con el código sospechoso
////////////////////////////////////////////////////////////////////////////////
{
  //Creación de variables
  Real initial_objects = ?;
  Real lost_objects = ?;
  Real iter = 1;
  
  //Conteo inicial
  Real initial_objects := Copy(NObject);
  
  //Encapsulación del código
  Real While(iter<=maxIter, 
  {
    //Código de pruebas susceptible de pérdida de objetos
    Real code(?);
    iter := iter+1
  });
  
  //Recuento final
  Real lost_objects := Copy(NObject) - initial_objects;
  
  WriteLn("lost objects = "<< lost_objects+" in "<<maxIter+" iterations" );
  WriteLn("lost objects by iteration= "<< (lost_objects/maxIter) );
  lost_objects
};

De esta forma podemos constatar fácilmente que no da lo mismo donde se declara el TimeSet de un objeto Serie. En el siguiente código lost.1==3 mientras que lost.2==0 gracias a que el fechado se ha creado en un ámbito superior al de la serie:

Real lost.1 = check_memory_leak_in_cycle(3,Real(Real void)
{
  TimeSet aux = C;
  Serie CalInd(W, aux);  
  0
});

Real lost.2 = 
{
  TimeSet aux = C;
  check_memory_leak_in_cycle(3,Real(Real void)
  {
    Serie CalInd(W, aux);  
    0
  })
};

comment:4 Changed 14 years ago by Víctor de Buen Remiro

(In [3286]) Refs #1121
Nueva función para el chequeo de agujeros de memoria:

Real CheckMemoryLeakInCycle (Real maxIter, Code code)

Chequea si cierto código TOL se deja objetos sin borrar en un ciclo de longitud dada.
Argumentos:

Real maxIter: Número de iteraciones
Code code: Función de API Real(Real void) con el código sospechoso

Retorna el número de objetos perdidos en el total de iteraciones.

comment:5 Changed 14 years ago by Pedro Gea

Vale, ya veo, la cosa es más común de lo que me había parecido al principio.

De hecho parece que la serie no permite que se destruya el timeset de ninguna forma, incluso cuando se nombra o asigna internamente, y es claro que nadie más puede hacer uso de dicho timeset:

Real N1 = Copy(NObject);
Real {
  Serie CalInd(W, D(1));
0};
Real N2 = Copy(NObject);
WriteLn(""<<Real (N2-N1-2)); //> 2

Respecto a lo de usar el nombre del timeset, eso soluciona el problema siempre que el timeset pasado como argumento sea un timeset definido globalmente y no localmente, véase por ejemplo:

Real N1 = Copy(NObject);

Real {
  TimeSet C2 = C;
  Serie CalIndW(Text dtn) { CalInd(W, Eval(dtn)) };
  Serie CalIndW("C2");
0};

Real N2 = Copy(NObject);
WriteLn(""<<Real (N2-N1-2)); //> 1

Y claro, las funciones que se hagan pasando el timeset por nombre dejarán de admitir expresiones del tipo: CalIndW(D(1)) .

comment:6 Changed 14 years ago by Víctor de Buen Remiro

No hace falta que tomes atajos por el momento porque creo que estoy cerca de resolverlo gracias a que han desaparecido otros problemas colaterales que permiten ver mejor lo que está pasando.

comment:7 Changed 14 years ago by Víctor de Buen Remiro

(In [3288]) Refs #1121

comment:8 Changed 14 years ago by Víctor de Buen Remiro

(In [3292]) Refs #1111
Refs #1121
Refs #1126
Recompiling CppTools to be compatible with v2.0.1 b.0.60.alpha

comment:9 Changed 14 years ago by Víctor de Buen Remiro

(In [3302]) Refs #1111
Refs #1121
Refs #1126
Recompiling CppTools to be compatible with v2.0.1 b.0.60.alpha

comment:10 Changed 14 years ago by Víctor de Buen Remiro

(In [3303]) Refs #1111
Refs #1121
Refs #1126
Recompiling CppTools to be compatible with v2.0.1 b.0.60.alpha

comment:11 Changed 14 years ago by Víctor de Buen Remiro

Resolution: fixed
Status: acceptedclosed

(In [3304]) Fixes #1111
Fixes #1121
New virtual method GetSizeOf due to sizeof(*address_) is not working fine

comment:12 Changed 14 years ago by Víctor de Buen Remiro

(In [3305]) Refs #1111
Refs #1121
Fixing bugs wich were hiding by solved memory leak

Note: See TracTickets for help on using tickets.