Opened 15 years ago
Closed 15 years ago
#755 closed defect (fixed)
The _this is lost
Reported by: | Owned by: | Víctor de Buen Remiro | |
---|---|---|---|
Priority: | highest | Milestone: | OOP Implementation |
Component: | OOP | Version: | |
Severity: | blocker | Keywords: | |
Cc: |
Description
Hola,
adjunto un archivo con un error que nos ha estado trayendo de cabeza.
Aunque parezca una tontería ha costado muchas horas de localizar y aislar, pero ya lo hemos conseguido.
Según me da la impresión el "puntero" _this hace referencia a la última referencia que se haga de un objeto y cuando esta se destruye vuelve a la última referencia viva.
Pero algo debe ocurrir cuando se crean los objetos en espacios locales, luego se referencian y esta referencia se destruye...
No sé si es éste exactamente el problema, pero el caso es que en MMS y los proyectos relacionados, que tienen un buen número de clases e instancias, unas instancias creían ser otras y el lío era un buen lío de verdad.
Personalmente me gustaría entender un poco mejor qué estaba ocurriendo con estas referencias. Un saludo
Attachments (1)
Change History (5)
Changed 15 years ago by
Attachment: | ticket_13.TheThisIsLost.tol added |
---|
comment:1 Changed 15 years ago by
Status: | new → accepted |
---|
comment:2 Changed 15 years ago by
Quizá la solución más natural sería que en la llamada a un miembro del NameBlock se enviara como argumento al BNameBlock el NameBlock desde el que se llama.
Pues si se accede al BNameBlock, algún NameBlock encapsulador quedará por ahí.
Aunque en la mayoría de acceso a miembros el _this no se use para nada y pasar este argumento puede ser un poco inutil.
Tampoco sé qué tan fácil o dificil es de implementar esta idea o si podría resultar engorrosa o ineficaz.
Otra idea es que el BNameBlock almacenara todos los NameBlock que lo encapsulan, y así tendría múltiples owners. Lo que no sé es si al destruirse un NameBlock podría borrarse de su BNameBlock o si se podría verificar cuales de los NameBlock no han sido destruidos y llamar al primero de ellos que esté vivo. Pues alguno vivo tiene que quedar.
comment:3 Changed 15 years ago by
En cuanto a la primera idea estamos en las mismas pues no hay ninguna forma sencilla de saber quién es el NameBlock que llama. Eso está en manos del reconocedor sintáctico. Quizás podría resolverse interviniendo en el operador ::, que es el único medio de acceso a miembros desde el exterior del NameBlock, y que sí que sabe cuál es el contenedor a la izquierda del ::, por lo que podría ocuparse de actualizar una variable global en C++ con el _this actual. Pero no se trata de un cambio sencillo y no puedo estar seguro de que no se me escape algo. Si fallan otras cosas más sencillas lo intentaré.
La segunda idea no la veo nada sencilla pues muy complicado saber si un objeto C++ está aún vivo. El sistema de asignación dinámica de memoria de C++ no informa en absoluto sobre el estado actual de una posición de RAM pues si lo hiciera sería muy lento (es lo que pasa en modo DEBUG). En TOL tenemos nuestro propio sistema de asignación de RAM especializado que hace algo al respecto pero no es infalible si no se almacena cierta información auxiliar que haría bastante complicada la implementación y también bastante lento el proceso.
De hecho parece que me está funcionando
comment:4 Changed 15 years ago by
Resolution: | → fixed |
---|---|
Status: | accepted → closed |
Los objetos TOL que ve el usuario son sólo cápsulas que contienen los objetos C++, los objetos Text encapsulan BText, los Real a BDat, etc. Por decirlo de alguna forma sencilla, los encapsuladores se ocupan de implementar la sintaxis de TOL que es común a todos los tipos de datos, mientras que los encapsulados definen la semántica que es particular en cada uno.
En el caso del tipo TOL NameBlock el tipo encapsulado es BNameBlock que tienen entre otras cosas métodos para buscar por nombre en un hash interno. En particular, el método
está cortocircuitado de forma que si memberName=="_this" entonces se devuelve el puntero del objeto TOL encapsulador del BNameBlock, que está dado por su miembro
Esto es bastante excepcional porque en el resto de tipos de datos los objetos encapsulados desconocen quién es su encapsulador ya que no lo necesitan para nada. Por este motivo no había nada en el motor de TOL capaz de manejar este tipo de referencias por lo que hubo que apañar la función principal evaluador de TOL
BGrammar::EvaluateTree
para que detectara la creación de objetos NameBlock para inocularles el encalpsulador a sus encapsulados en el campoowner_
.El problema es que cuando se hacen varias referencias a un mismo NameBlock hay un único BNameBlock que tiene varios contenedores TOL y la situación es ambigua. Esto no es en general problemático ya que el
owner_
sólo se usa para proporcionar el_this
y al fin y al cabo da lo mismo operar sobre cualquiera de sus contenedores pues los resultados no varían. El problema aparece efectivamente cuando se hace una referencia local pues al salir de su ámbito el contenedor es destruido y el BNameBlock cree no pertenecer a nadie por lo que el_this
no funciona, y podría dar lugar incluso a caídas del sistema.La solución adoptada en principio era no cambiar el owner_ si ya ha sido asignado previamente a un contenedor de un ámbito superior al actual, pero eso no sirve de mucho cuando en este caso en realidad el contenedor también ha sido creado en un ámbito local.
Voy a tratar de ver qué pasa si nunca se reasigna el _owner, pues es posible que con eso sea suficiente.