Tenga en cuenta que no quiero para resolver cualquier problema con mi pregunta - Estaba pensando acerca de las probabilidades de que las cosas sucedan y por lo tanto se preguntaba sobre algo:¿Qué sucede exactamente si elimina un objeto? (GCC) (Cuando doble eliminar los accidentes?)
¿Qué sucede exactamente si eliminas el objeto y usas gcc como compilador?
La semana pasada estaba investigando un bloqueo, donde una condición de carrera condujo a una eliminación doble de un objeto.
El bloqueo se produjo al llamar al destructor virtual del objeto, porque el puntero a la tabla de funciones virtuales ya se sobrescribió.
¿El puntero de función virtual se sobrescribe con la primera eliminación?
En caso negativo, ¿es seguro el segundo borrado entonces, siempre que no se realice una nueva asignación de memoria mientras tanto?
Me pregunto por qué el problema que tenía no se reconoció antes y la única explicación es que la tabla de funciones virtuales se sobrescribe inmediatamente durante la primera eliminación o la segunda eliminación no se bloquea.
(La primera significa que el bloqueo siempre ocurre en el mismo lugar si ocurre la "carrera" -el segundo, que normalmente no sucede cuando la carrera ocurre- y solo si un tercer hilo sobrescribe el objeto eliminado en el Mientras tanto, el problema se produce)
Editar/actualización:.
hice una prueba, el siguiente código se bloquea con un error de segmentación (gcc 4.4, i686 y AMD64):
class M
{
private:
int* ptr;
public:
M() {
ptr = new int[1];
}
virtual ~M() {delete ptr;}
};
int main(int argc, char** argv)
{
M* ptr = new M();
delete ptr;
delete ptr;
}
Si elimino el 'virtual' del controlador, el programa se cancela con glibc porque detecta el doble libre. Con 'virtual', el bloqueo se produce cuando se realiza la llamada de función indirecta al destructor, porque el puntero a la tabla de funciones virtuales no es válido.
En ambos amd64 e i686 el puntero apunta a una región de memoria válida (pila), pero el valor no es válido (¿contador? Es muy bajo, por ejemplo 0x11 o 0x21) por lo que la 'llamada' (o 'jmp 'cuando el compilador realizó una optimización de retorno) salta a una región no válida.
Programarecibido SIGSEGV señal,
Fallo de segmentación. 0x0000000000000021
in ??() (gdb)
#
0 0x0000000000000021 in ??()
#
1 0x000000000040083e en main()
Así que con las condiciones antes mencionadas, el puntero a la tabla de función virtual está siempre sobrescrito por el primer eliminar, por lo que la próxima borrado saltará al nirvana si la clase tiene un destructor virtual.
Suena como que necesita para invertir en algún mutex o secciones críticas demasiado –
0A0D: Este es mi solución prelimary (solución). De hecho, hubo una falla de diseño, porque se pensó que hay dos hilos que pueden eliminar el objeto. – IanH